Skip to content

Commit

Permalink
IStrategy.NotReadyForHardWork; vaults 1.2.0: doHardWorkOnDeposit init
Browse files Browse the repository at this point in the history
  • Loading branch information
a17 committed Mar 5, 2024
1 parent 60f459b commit 35222f6
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 34 deletions.
10 changes: 7 additions & 3 deletions src/core/base/VaultBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ abstract contract VaultBase is Controllable, ERC20Upgradeable, ReentrancyGuardUp
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Version of VaultBase implementation
string public constant VERSION_VAULT_BASE = "1.1.0";
string public constant VERSION_VAULT_BASE = "1.2.0";

/// @dev Delay between deposits/transfers and withdrawals
uint internal constant _WITHDRAW_REQUEST_BLOCKS = 5;
Expand Down Expand Up @@ -88,7 +88,7 @@ abstract contract VaultBase is Controllable, ERC20Upgradeable, ReentrancyGuardUp
$.strategy = IStrategy(strategy_);
$.tokenId = tokenId_;
__ReentrancyGuard_init();
$.doHardWorkOnDeposit = true;
$.doHardWorkOnDeposit = IStrategy(strategy_).isHardWorkOnDepositAllowed();
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand Down Expand Up @@ -189,7 +189,11 @@ abstract contract VaultBase is Controllable, ERC20Upgradeable, ReentrancyGuardUp

// slither-disable-start timestamp
// nosemgrep
if ($.doHardWorkOnDeposit && block.timestamp > v.strategy.lastHardWork() + _MIN_HARDWORK_DELAY) {
if (
$.doHardWorkOnDeposit
&& block.timestamp > v.strategy.lastHardWork() + _MIN_HARDWORK_DELAY
&& v.strategy.isReadyForHardWork()
) {
// slither-disable-end timestamp
v.strategy.doHardWork();
}

Check notice

Code scanning / Semgrep OSS

Semgrep Finding: rules.solidity.performance.use-nested-if Note

Using nested is cheaper than using && multiple check combinations.
There are more advantages, such as easier to read code and better coverage reports.
Expand Down
2 changes: 1 addition & 1 deletion src/core/vaults/CVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ contract CVault is VaultBase {
//region ----- Constants -----

/// @dev Version of CVault implementation
string public constant VERSION = "1.1.0";
string public constant VERSION = "1.2.0";

uint internal constant _UNIQUE_INIT_ADDRESSES = 1;

Expand Down
2 changes: 1 addition & 1 deletion src/core/vaults/RMVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract RMVault is RVaultBase, IManagedVault {
//region ----- Constants -----

/// @dev Version of RMVault implementation
string public constant VERSION = "1.1.0";
string public constant VERSION = "1.2.0";

uint internal constant _UNIQUE_INIT_ADDRESSES = 1;

Expand Down
2 changes: 1 addition & 1 deletion src/core/vaults/RVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract RVault is RVaultBase {
//region ----- Constants -----

/// @dev Version of RVault implementation
string public constant VERSION = "1.1.0";
string public constant VERSION = "1.2.0";

uint public constant BB_TOKEN_DURATION = 86400 * 7;

Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/IStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ interface IStrategy is IERC165 {
uint multisigReceiverFee
);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error NotReadyForHardWork();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
6 changes: 5 additions & 1 deletion src/strategies/base/StrategyBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ abstract contract StrategyBase is Controllable, IStrategy {
function _beforeTransferAssets() internal virtual {}

/// @dev Can be overrided by derived base strategies for custom logic
function _beforeDoHardWork() internal virtual {}
function _beforeDoHardWork() internal virtual {
if (!IStrategy(this).isReadyForHardWork()) {
revert NotReadyForHardWork();
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Must be implemented by derived contracts */
Expand Down
32 changes: 23 additions & 9 deletions test/base/UniversalTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ abstract contract UniversalTest is Test, ChainSetup, Utils {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HARDWORK 0 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
assertEq(strategy.isReadyForHardWork(), true);
assertEq(strategy.isReadyForHardWork(), true, "Not ready for HardWork");
vm.txGasPrice(15e10); // 150gwei
{
vars.apr = 0;
Expand Down Expand Up @@ -431,15 +431,25 @@ abstract contract UniversalTest is Test, ChainSetup, Utils {

StrategyLib.computeApr(tempTvl, tempEarned, tempDuration);

assertGt(tempApr, 0);
assertGt(tempEarned, 0);
assertGt(tempTvl, 0);
assertGt(tempDuration, 0);
assertGt(tempApr, 0, "HardWork APR");
assertGt(tempEarned, 0, "HardWork Earned");
assertGt(tempTvl, 0, "HardWork TVL");
assertGt(tempDuration, 0, "HardWork duration");
}
}
require(vars.hwEventFound, "UniversalTest: HardWork event not emited");
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* NO EMPTY HARDWORKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
if (CommonLib.eq(strategy.strategyLogicId(), StrategyIdLib.DEFIEDGE_QUICKSWAP_MERKL_FARM)) {
vm.startPrank(address(vars.hardWorker));
vm.expectRevert(abi.encodeWithSelector(IStrategy.NotReadyForHardWork.selector));
IVault(vars.vault).doHardWork();
vm.stopPrank();
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADD REWARDS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand All @@ -452,11 +462,11 @@ abstract contract UniversalTest is Test, ChainSetup, Utils {
address rewardToken = vars.isRVault ? vars.allowedBBTokens[0] : platform.targetExchangeAsset();
uint balanceBefore = IERC20(rewardToken).balanceOf(address(this));
IRVault(vars.vault).getAllRewards();
assertGt(IERC20(rewardToken).balanceOf(address(this)), balanceBefore);
assertGt(IERC20(rewardToken).balanceOf(address(this)), balanceBefore, "Rewards was not claimed");
skip(3600);
balanceBefore = IERC20(rewardToken).balanceOf(address(this));
IRVault(vars.vault).getAllRewards();
assertGt(IERC20(rewardToken).balanceOf(address(this)), balanceBefore);
assertGt(IERC20(rewardToken).balanceOf(address(this)), balanceBefore, "Rewards was not claimed after skip time");
balanceBefore = IERC20(rewardToken).balanceOf(address(this));
IRVault(vars.vault).getReward(0);
assertEq(IERC20(rewardToken).balanceOf(address(this)), balanceBefore);
Expand Down Expand Up @@ -490,9 +500,13 @@ abstract contract UniversalTest is Test, ChainSetup, Utils {
assertEq(valueOut, totalWas, "previewDepositAssets by underlying valueOut");
uint lastHw = strategy.lastHardWork();
IVault(tempVault).depositAssets(underlyingAssets, underlyingAmounts, 0, address(0));
assertGt(strategy.lastHardWork(), lastHw);
if (strategy.isHardWorkOnDepositAllowed()) {
assertGt(strategy.lastHardWork(), lastHw, "HardWork not happened");
assertGt(strategy.total(), totalWas, "Strategy total not increased after HardWork");
}

assertEq(IERC20(underlying).balanceOf(address(this)), 0);
assertGt(strategy.total(), totalWas);

uint vaultBalance = IERC20(tempVault).balanceOf(address(this));
assertEq(
vaultBalance,
Expand Down
81 changes: 65 additions & 16 deletions test/core/Platform.Polygon.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "../../src/strategies/libs/ALMPositionNameLib.sol";

contract PlatformPolygonTest is PolygonSetup {
address internal constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address internal constant USD = address(840);

struct BuildingVars {
uint len;
Expand Down Expand Up @@ -87,6 +88,17 @@ contract PlatformPolygonTest is PolygonSetup {
}

function testAll() public {
// change heartbeat to prevent "OLD_PRICE revert" for DefiEdge underlings
IDefiEdgeStrategyFactory defiEdgeFactory = IDefiEdgeStrategyFactory(0x730d158D29165C55aBF368e9608Af160DD21Bd80);
address gov = defiEdgeFactory.governance();
vm.startPrank(gov);
defiEdgeFactory.setMinHeartbeat(PolygonLib.TOKEN_WETH, USD, 86400 * 365);
defiEdgeFactory.setMinHeartbeat(PolygonLib.TOKEN_WMATIC, USD, 86400 * 365);
defiEdgeFactory.setMinHeartbeat(PolygonLib.TOKEN_USDCe, USD, 86400 * 365);
defiEdgeFactory.setMinHeartbeat(PolygonLib.TOKEN_WETH, ETH, 86400 * 365);
defiEdgeFactory.setMinHeartbeat(PolygonLib.TOKEN_WMATIC, ETH, 86400 * 365);
vm.stopPrank();

// disable deprecated strategies
// vm.startPrank(platform.multisig());
// platform.addOperator(platform.multisig());
Expand Down Expand Up @@ -215,6 +227,8 @@ contract PlatformPolygonTest is PolygonSetup {
(string[] memory descEmpty,,,,,,,,) = factory.whatToBuild();
assertEq(descEmpty.length, 0);

address[] memory stategyRevenueAssets;

IVaultManager vaultManager = IVaultManager(platform.vaultManager());
// deposit to all vaults
{
Expand All @@ -238,24 +252,27 @@ contract PlatformPolygonTest is PolygonSetup {
console.log(string.concat(" ", symbol[i]));

_depositToVault(vaultAddress[i], 1e21);

IStrategy strategy = IVault(vaultAddress[i]).strategy();
_fillStrategyRewards(strategy);
}
}

IHardWorker hw = IHardWorker(platform.hardWorker());
bool canExec;
bytes memory execPayload;
(canExec, execPayload) = hw.checkerServer();
assertEq(canExec, false);
assertEq(canExec, false, "Must cant exec");

skip(1 days);

(canExec, execPayload) = hw.checkerServer();
assertEq(canExec, true);
assertEq(canExec, true, "Must exec");
vm.expectRevert(abi.encodeWithSelector(IHardWorker.NotServerOrGelato.selector));
vm.prank(address(666));
(bool success,) = address(hw).call(execPayload);
(success,) = address(hw).call(execPayload);
assertEq(success, false);
assertEq(success, false, "Must be not success");
vm.expectRevert(abi.encodeWithSelector(IControllable.NotGovernanceAndNotMultisig.selector));
hw.setDedicatedServerMsgSender(address(this), true);
assertEq(hw.maxHwPerCall(), 5);
Expand Down Expand Up @@ -308,31 +325,38 @@ contract PlatformPolygonTest is PolygonSetup {
hw.setMaxHwPerCall(5);

(success,) = address(hw).call(execPayload);
assertEq(success, true);
assertEq(success, true, "HardWorker call not success");

skip(1 days);

(canExec, execPayload) = hw.checkerGelato();
assertEq(canExec, true);
assertEq(canExec, true, "HardWorker call not success 2");
vm.startPrank(hw.dedicatedGelatoMsgSender());

vm.deal(address(hw), 0);
vm.expectRevert(abi.encodeWithSelector(IHardWorker.NotEnoughETH.selector));
(success,) = address(hw).call(execPayload);

vm.deal(address(hw), 2e18);
_fillAllStrategiesRewards(vaultManager);
vm.deal(address(hw), 2000e18);
(success,) = address(hw).call(execPayload);
assertEq(success, true);
assertEq(success, true, "HardWorker call not success 3");
assertGt(hw.gelatoBalance(), 0);
vm.stopPrank();

for (uint i; i < len; ++i) {
(canExec, execPayload) = hw.checkerServer();
if (canExec) {
(success,) = address(hw).call(execPayload);
assertEq(success, true);
} else {
break;
skip(1 days);
{
_fillAllStrategiesRewards(vaultManager);

for (uint i; i < len; ++i) {
(canExec, execPayload) = hw.checkerServer();
if (canExec) {
bytes memory str;
(success,str) = address(hw).call(execPayload);
assertEq(success, true, "Not success");
} else {
break;
}
}
}

Expand All @@ -350,8 +374,11 @@ contract PlatformPolygonTest is PolygonSetup {
address vault_ = factory.deployedVault(factory.deployedVaultsLength() - 1);
vaultsForHardWork[0] = vault_;

(stategyRevenueAssets, ) = IVault(vault_).strategy().getRevenue();
deal(stategyRevenueAssets[0], address(IVault(vault_).strategy()), 1e14);

vm.txGasPrice(15e10);
deal(address(hw), type(uint).max);
vm.deal(address(hw), 2000e18);
vm.expectRevert(abi.encodeWithSelector(IControllable.ETHTransferFailed.selector));
hw.call(vaultsForHardWork);
canReceive = true;
Expand All @@ -362,16 +389,21 @@ contract PlatformPolygonTest is PolygonSetup {

//lower
deal(address(hw), 0);
deal(stategyRevenueAssets[0], address(IVault(vault_).strategy()), 1e14);
assertGt(hw.gelatoMinBalance(), address(hw).balance);
// vm.expectRevert(abi.encodeWithSelector(IHardWorker.NotEnoughETH.selector));
hw.call(vaultsForHardWork);

//equal
deal(address(hw), hw.gelatoMinBalance());
deal(stategyRevenueAssets[0], address(IVault(vault_).strategy()), 1e14);
assertEq(address(hw).balance, hw.gelatoMinBalance());
// vm.expectRevert(abi.encodeWithSelector(IHardWorker.NotEnoughETH.selector));
hw.call(vaultsForHardWork);

//higher
deal(address(hw), type(uint).max);
deal(stategyRevenueAssets[0], address(IVault(vault_).strategy()), 1e14);
assertGt(address(hw).balance, hw.gelatoMinBalance());
hw.call(vaultsForHardWork);

Expand All @@ -380,7 +412,7 @@ contract PlatformPolygonTest is PolygonSetup {
skip(1 hours);
skip(100);
(canExec,) = hw.checkerGelato();
assertEq(canExec, false);
assertEq(canExec, false, "Must not exec");
(canExec,) = hw.checkerServer();
assertEq(canExec, true);
}
Expand Down Expand Up @@ -475,4 +507,21 @@ contract PlatformPolygonTest is PolygonSetup {
vaultInitNums[4] = 1000e6;
vaultInitNums[5] = 50_000;
}

function _fillAllStrategiesRewards(IVaultManager vaultManager) internal {
(address[] memory vaultAddress,,,,,,,,,) = vaultManager.vaults();
for (uint i; i < vaultAddress.length; ++i) {
IStrategy strategy = IVault(vaultAddress[i]).strategy();
_fillStrategyRewards(strategy);
}
}

function _fillStrategyRewards(IStrategy strategy) internal {
(address[] memory stategyRevenueAssets, ) = strategy.getRevenue();
if (CommonLib.eq("QuickSwap Static Merkl Farm", strategy.strategyLogicId())) {
deal(stategyRevenueAssets[2], address(strategy), 1e18);
} else {
deal(stategyRevenueAssets[0], address(strategy), 1e18);
}
}
}
9 changes: 8 additions & 1 deletion test/core/Platform.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Platform} from "../../src/core/Platform.sol";
import "../../src/core/proxy/Proxy.sol";
import "../../src/core/vaults/CVault.sol";
import "../../src/test/MockVaultUpgrade.sol";
import "../../src/test/MockStrategy.sol";
import "../../src/core/Factory.sol";
import "../../src/core/Swapper.sol";
import "../../src/core/StrategyLogic.sol";
Expand All @@ -16,11 +17,17 @@ import "../../src/strategies/libs/StrategyDeveloperLib.sol";
contract PlatformTest is Test {
Platform public platform;
StrategyLogic public strategyLogic;
MockStrategy public strategyImplementation;
MockStrategy public strategy;

function setUp() public {
Proxy proxy = new Proxy();
proxy.initProxy(address(new Platform()));
platform = Platform(address(proxy));
strategyImplementation = new MockStrategy();
Proxy strategyProxy = new Proxy();
strategyProxy.initProxy(address(strategyImplementation));
strategy = MockStrategy(address(strategyProxy));
}

function testSetMinTvlForFreeHardWork() public {
Expand Down Expand Up @@ -171,7 +178,7 @@ contract PlatformTest is Test {
vault.initialize(
IVault.VaultInitializationData({
platform: address(platform),
strategy: address(0),
strategy: address(strategy),
name: "V",
symbol: "V",
tokenId: 0,
Expand Down
Loading

0 comments on commit 35222f6

Please sign in to comment.