Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion contracts/CompoundV3InvestStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {IInvestStrategy} from "./interfaces/IInvestStrategy.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
import {IExposeStorage} from "./interfaces/IExposeStorage.sol";
import {InvestStrategyClient} from "./InvestStrategyClient.sol";
import {MSVBase} from "./MSVBase.sol";

/**
* @title CompoundV3InvestStrategy
Expand Down Expand Up @@ -149,7 +150,8 @@ contract CompoundV3InvestStrategy is IInvestStrategy {

uint256 earned = IERC20(reward).balanceOf(address(this));
uint256 reinvestAmount = swapConfig.exactInput(reward, _baseToken, earned, price);
_supply(reinvestAmount);

MSVBase(address(this)).depositToStrategies(reinvestAmount);
emit RewardsClaimed(reward, earned, reinvestAmount);
}

Expand Down
13 changes: 13 additions & 0 deletions contracts/MSVBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ abstract contract MSVBase is IExposeStorage {
if (left != 0) revert DepositError(); // This shouldn't happen, since assets must be <= maxDeposit(owner)
}

/**
* @dev Deposit assets to the strategies in the deposit queue order until zero assets remains to be deposited.
* After finishing the deposit, left must be zero, otherwise reverts, and should never happen.
*
* This method might be used by the some strategies to reinject rewards. Left as virtual so child classes
* can implement somekind of access control.
*
* @param assets The amount of assets to be deposited to the strategies.
*/
function depositToStrategies(uint256 assets) public virtual {
_depositToStrategies(assets);
}

/**
* @dev Exposes a given slot as a bytes array. To be used by the IInvestStrategy views to access their storage.
* Only the slot==strategyStorageSlot() can be accessed.
Expand Down
15 changes: 15 additions & 0 deletions contracts/MultiStrategyERC4626.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract MultiStrategyERC4626 is MSVBase, PermissionedERC4626 {
bytes32 public constant QUEUE_ADMIN_ROLE = keccak256("QUEUE_ADMIN_ROLE");
bytes32 public constant REBALANCER_ROLE = keccak256("REBALANCER_ROLE");
bytes32 public constant FORWARD_TO_STRATEGY_ROLE = keccak256("FORWARD_TO_STRATEGY_ROLE");
bytes32 public constant DEPOSIT_TO_STRATEGIES_ROLE = keccak256("DEPOSIT_TO_STRATEGIES_ROLE");

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand Down Expand Up @@ -209,4 +210,18 @@ contract MultiStrategyERC4626 is MSVBase, PermissionedERC4626 {
onlyRole(FORWARD_TO_STRATEGY_ROLE)
onlyRole(getForwardToStrategyRole(strategyIndex, method))
{}

/**
* @dev Deposit assets to the strategies in the deposit queue order until zero assets remains to be deposited.
* After finishing the deposit, left must be zero, otherwise reverts, and should never happen.
*
* This method might be used by the some strategies to reinject rewards. Requires DEPOSIT_TO_STRATEGIES_ROLE
* unless the call is made by one of the strategies (msg.sender == address(this))
*
* @param assets The amount of assets to be deposited to the strategies.
*/
function depositToStrategies(uint256 assets) public override {
if (msg.sender != address(this)) _checkRole(DEPOSIT_TO_STRATEGIES_ROLE);
_depositToStrategies(assets);
}
}
13 changes: 10 additions & 3 deletions test/test-compound-v3-vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ const variants = [
STRATEGY_ADMIN_ROLE: 4,
QUEUE_ADMIN_ROLE: 5,
FORWARD_TO_STRATEGY_ROLE: 6,
DEPOSIT_TO_STRATEGIES_ROLE: 7,
};

const vault = await hre.upgrades.deployProxy(
Expand Down Expand Up @@ -394,6 +395,7 @@ const variants = [
await setupAMRole(acMgr.connect(admin), vault, roles, "REBALANCER_ROLE", ["rebalance"]);

await setupAMRole(acMgr.connect(admin), vault, roles, "FORWARD_TO_STRATEGY_ROLE", ["forwardToStrategy"]);
await setupAMRole(acMgr.connect(admin), vault, roles, "DEPOSIT_TO_STRATEGIES_ROLE", ["depositToStrategies"]);

await acMgr.connect(admin).grantRole(roles.LP_ROLE, lp, 0);
await acMgr.connect(admin).grantRole(roles.LP_ROLE, lp2, 0);
Expand Down Expand Up @@ -692,6 +694,8 @@ variants.forEach((variant) => {
vault
);
await acMgr.connect(admin).grantRole(roles.FORWARD_TO_STRATEGY_ROLE, anon, 0);
// Grant DEPOSIT_TO_STRATEGIES_ROLE to the vault - for reinjection of rewards
await acMgr.connect(admin).grantRole(roles.DEPOSIT_TO_STRATEGIES_ROLE, vault, 0);
// Still fails because other role is missing
const specificSelector = await vault.getForwardToStrategySelector(0, CompoundV3StrategyMethods.harvestRewards);
await variant.accessControlCheck(variant.harvestRewards(vault.connect(anon), _A(100)), anon, null, vault);
Expand Down Expand Up @@ -817,9 +821,8 @@ variants.forEach((variant) => {
);

variant.tagit("Checks only authorized user can change swap config [!AAVEV3Strategy]", async () => {
const { currency, vault, admin, anon, lp, swapConfig, strategy, swapLibrary, acMgr } = await helpers.loadFixture(
variant.fixture
);
const { currency, vault, admin, anon, lp, swapConfig, strategy, swapLibrary, acMgr, roles } =
await helpers.loadFixture(variant.fixture);

expect(await variant.getSwapConfig(vault, strategy)).to.deep.equal(swapConfig);
await expect(vault.connect(lp).mint(_A(3000), lp)).not.to.be.reverted;
Expand Down Expand Up @@ -868,6 +871,10 @@ variants.forEach((variant) => {
);
await grantRole(hre, vault.connect(admin), specificRole, anon);
}
if (variant.accessManaged) {
// Grant DEPOSIT_TO_STRATEGIES_ROLE to the vault - for reinjection of rewards
await acMgr.connect(admin).grantRole(roles.DEPOSIT_TO_STRATEGIES_ROLE, vault, 0);
}

// Check validates new config
await expect(
Expand Down
7 changes: 5 additions & 2 deletions test/test-integration-fork.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,11 @@ describe("MultiStrategy Integration fork tests", function () {
ethers.AbiCoder.defaultAbiCoder().encode(["uint256"], [compUSD])
);

expect(await compoundStrategy.totalAssets(vault)).to.closeTo(_A("7070.852454"), CENT);
expect(await vault.totalAssets()).to.be.closeTo(_A("12131.977778"), CENT);
const rewards = _A("10.3328");

expect(await aaveStrategy.totalAssets(vault)).to.closeTo(_A("5061.125277") + rewards, CENT);
expect(await compoundStrategy.totalAssets(vault)).to.closeTo(_A("7060.519644"), CENT);
expect(await vault.totalAssets()).to.be.closeTo(_A("12121.644921") + rewards, CENT);

// Withdraw all the funds
await vault.connect(lp).redeem(_A(5000), lp, lp);
Expand Down
Loading