Skip to content

Commit

Permalink
⬆ uses the new gelato automation workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
The-Wary-One committed Aug 25, 2024
1 parent 8c57fa9 commit a7eeb93
Show file tree
Hide file tree
Showing 19 changed files with 77 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[submodule "lib/openzeppelin"]
path = lib/openzeppelin
url = https://github.com/OpenZeppelin/openzeppelin-contracts
branch = release-v5.0
branch = release-v5.1
[submodule "lib/self-repaying-eth"]
path = lib/self-repaying-eth
url = https://github.com/The-Wary-One/self-repaying-eth
Expand Down
4 changes: 2 additions & 2 deletions deployments/external.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"1": {
"01-alchemist": "0x062Bf725dC4cDF947aa79Ca2aaCCD4F385b13b5c",
"02-wethGateway": "0xA22a7ec2d82A471B1DAcC4B37345Cf428E76D67A",
"03-alETHPool": "0xC4C319E2D4d66CcA4464C0c2B32c9Bd23ebe784e",
"04-curveCalc": "0xc1DB00a8E5Ef7bfa476395cdbcc98235477cDE4E",
"03-alETHPool": "0x8eFD02a0a40545F32DbA5D664CbBC1570D3FedF6",
"04-weth": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"05-controller": "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5",
"06-registrar": "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
"07-gelatoAutomate": "0x2A6C106ae13B558BB9E2Ec64Bd2f1f7BEFF3A5E0"
Expand Down
2 changes: 1 addition & 1 deletion lib/alchemix
Submodule alchemix updated 54 files
+6 −0 .github/workflows/forge-tests.yml
+0 −1 .gitmodules
+21 −0 LICENSE
+5 −0 funding.json
+1 −1 lib/forge-std
+224 −6 src/AlchemicTokenV2Base.sol
+4 −14 src/AlchemistV2.sol
+5 −9 src/CrossChainCanonicalAlchemicTokenV2.sol
+3 −194 src/CrossChainCanonicalBase.sol
+2 −6 src/CrossChainCanonicalGALCX.sol
+793 −0 src/PoolAssetManager.sol
+1,066 −0 src/TwoPoolAssetManager.sol
+125 −0 src/adapters/lido/WstETHAdapterOptimism.sol
+31 −9 src/adapters/lido/WstETHAdapterV1.sol
+2 −2 src/interfaces/IERC20Burnable.sol
+20 −1 src/interfaces/IMigrationTool.sol
+5 −2 src/interfaces/IRewardCollector.sol
+18 −0 src/interfaces/IRewardRouter.sol
+0 −9 src/interfaces/alchemist/IAlchemistV2AdminActions.sol
+2 −0 src/interfaces/alchemist/IAlchemistV2State.sol
+1 −1 src/interfaces/external/chainlink/IChainlinkOracle.sol
+129 −0 src/interfaces/external/connext/IXERC20.sol
+6 −0 src/interfaces/external/convex/IConvexFraxBooster.sol
+11 −0 src/interfaces/external/convex/IConvexFraxFarm.sol
+10 −0 src/interfaces/external/convex/IConvexFraxVault.sol
+8 −0 src/interfaces/external/convex/IConvexStakingWrapper.sol
+2 −0 src/interfaces/external/curve/IStableMetaPool.sol
+1 −1 src/interfaces/external/curve/IStableSwap2Pool.sol
+6 −0 src/interfaces/external/jones/IJonesDaoGlpVault.sol
+6 −0 src/interfaces/external/jones/IJonesStableVault.sol
+1 −2 src/interfaces/keepers/IAlchemixHarvester.sol
+12 −10 src/keepers/AlchemixHarvester.sol
+6 −97 src/keepers/HarvestResolver.sol
+2 −2 src/libraries/TokenUtils.sol
+68 −2 src/migration/MigrationTool.sol
+98 −55 src/test/AaveV3TokenAdapter.t.sol
+85 −0 src/test/Harvester.t.sol
+16 −1 src/test/MigrationToolETH.t.sol
+131 −0 src/test/POC.t.sol
+443 −0 src/test/PoolAssetManager.t.sol
+532 −0 src/test/TwoPoolAssetManager.t.sol
+34 −26 src/test/VesperAdapterV1.t.sol
+80 −0 src/test/WstETHAdapterOptimism.t.sol
+2 −4 src/test/WstETHAdapterV1.t.sol
+44 −0 src/test/XTokens.t.sol
+2 −2 src/test/mocks/TestERC20.sol
+14 −0 src/test/utils/users/PoolAssetManagerUser.sol
+14 −0 src/test/utils/users/TwoPoolAssetManagerUser.sol
+0 −66 src/utils/RewardCollectorOptimism.sol
+0 −108 src/utils/RewardCollectorVesper.sol
+105 −0 src/utils/RewardRouter.sol
+99 −0 src/utils/collectors/OptimismAaveRewardCollector.sol
+162 −0 src/utils/collectors/VesperRewardCollector.sol
+7 −3 test.sh
2 changes: 1 addition & 1 deletion lib/gelato
Submodule gelato updated 1345 files
2 changes: 1 addition & 1 deletion lib/openzeppelin
Submodule openzeppelin updated 885 files
19 changes: 6 additions & 13 deletions script/DeploySRENS.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
BaseRegistrarImplementation,
ETHRegistrarController,
IAlchemistV2,
ICurveCalc,
ICurvePool,
ICurveStableSwapNG,
IWETH9,
Automate,
SelfRepayingENS
} from "../src/SelfRepayingENS.sol";
Expand All @@ -24,7 +24,7 @@ contract DeploySRENS is Script {

// Deploy the contract.
return deploy(
config.controller, config.registrar, config.gelatoAutomate, config.alchemist, config.alETHPool, config.curveCalc
config.controller, config.registrar, config.gelatoAutomate, config.alchemist, config.alETHPool, config.weth
);
}

Expand All @@ -34,19 +34,12 @@ contract DeploySRENS is Script {
BaseRegistrarImplementation registrar,
Automate gelatoAutomate,
IAlchemistV2 alchemist,
ICurvePool alETHPool,
ICurveCalc curveCalc
ICurveStableSwapNG alETHPool,
IWETH9 weth
) public returns (SelfRepayingENS) {
// Deploy the SRENS contract.
vmSafe.broadcast();
SelfRepayingENS srens = new SelfRepayingENS(
controller,
registrar,
gelatoAutomate,
alchemist,
alETHPool,
curveCalc
);
SelfRepayingENS srens = new SelfRepayingENS(controller, registrar, gelatoAutomate, alchemist, alETHPool, weth);

return srens;
}
Expand Down
8 changes: 5 additions & 3 deletions script/Toolbox.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {Script, stdJson} from "../lib/forge-std/src/Script.sol";
import {Automate} from "../lib/gelato/contracts/Automate.sol";
import {Whitelist} from "../lib/alchemix/src/utils/Whitelist.sol";
import {WETHGateway} from "../lib/alchemix/src/WETHGateway.sol";
import {IAlchemistV2, ICurveCalc, ICurvePool} from "../lib/self-repaying-eth/src/SelfRepayingETH.sol";
import {
IAlchemistV2, ICurveStableSwapNG, IWETH9, SelfRepayingETH
} from "../lib/self-repaying-eth/src/SelfRepayingETH.sol";

import {BaseRegistrarImplementation, ETHRegistrarController, SelfRepayingENS} from "../src/SelfRepayingENS.sol";

Expand All @@ -17,8 +19,8 @@ contract Toolbox is Script {
struct Config {
IAlchemistV2 alchemist;
WETHGateway wethGateway;
ICurvePool alETHPool;
ICurveCalc curveCalc;
ICurveStableSwapNG alETHPool;
IWETH9 weth;
ETHRegistrarController controller;
BaseRegistrarImplementation registrar;
Automate gelatoAutomate;
Expand Down
9 changes: 2 additions & 7 deletions script/ToolboxLocal.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract ToolboxLocal is Toolbox {
pure
returns (LibDataTypes.ModuleData memory moduleData)
{
moduleData = LibDataTypes.ModuleData({modules: new LibDataTypes.Module[](1), args: new bytes[](1)});
moduleData = LibDataTypes.ModuleData({modules: new LibDataTypes.Module[](2), args: new bytes[](2)});

moduleData.modules[0] = LibDataTypes.Module.RESOLVER;
moduleData.modules[1] = LibDataTypes.Module.PROXY;
Expand All @@ -36,12 +36,7 @@ contract ToolboxLocal is Toolbox {
Toolbox.Config memory config = getConfig();

SelfRepayingENS srens = new SelfRepayingENS(
config.controller,
config.registrar,
config.gelatoAutomate,
config.alchemist,
config.alETHPool,
config.curveCalc
config.controller, config.registrar, config.gelatoAutomate, config.alchemist, config.alETHPool, config.weth
);

return srens;
Expand Down
33 changes: 22 additions & 11 deletions src/SelfRepayingENS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import {
import {Multicall} from "../lib/openzeppelin/contracts/utils/Multicall.sol";
/* --- Gelato --- */
import {Automate, LibDataTypes} from "../lib/gelato/contracts/Automate.sol";
/* --- Self Repaying ENS --- */
import {IAlchemistV2, ICurveCalc, ICurvePool, SelfRepayingETH} from "../lib/self-repaying-eth/src/SelfRepayingETH.sol";
import {ProxyModule} from "../lib/gelato/contracts/taskModules/ProxyModule.sol";
import {IOpsProxyFactory} from "../lib/gelato/contracts/interfaces/IOpsProxyFactory.sol";
/* --- Self Repaying ETH --- */
import {
IAlchemistV2, ICurveStableSwapNG, IWETH9, SelfRepayingETH
} from "../lib/self-repaying-eth/src/SelfRepayingETH.sol";
/* --- Solmate --- */
import {toDaysWadUnsafe, wadDiv, wadExp} from "../lib/solmate/src/utils/SignedWadMath.sol";

Expand All @@ -39,6 +43,9 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {
/// @notice The Gelato automate contract.
Automate immutable gelatoAutomate;

/// @notice The Gelato dedicated caller.
address immutable gelatoDedicatedCaller;

/// @notice The Gelato address for ETH.
address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

Expand Down Expand Up @@ -79,14 +86,17 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {
BaseRegistrarImplementation _registrar,
Automate _gelatoAutomate,
IAlchemistV2 _alchemist,
ICurvePool _alETHPool,
ICurveCalc _curveCalc
) payable SelfRepayingETH(_alchemist, _alETHPool, _curveCalc) {
ICurveStableSwapNG _alETHPool,
IWETH9 _weth
) payable SelfRepayingETH(_alchemist, _alETHPool, _weth) {
controller = _controller;
registrar = _registrar;
gelatoAutomate = _gelatoAutomate;

gelato = _gelatoAutomate.gelato();
address proxyModuleAddress = _gelatoAutomate.taskModuleAddresses(LibDataTypes.Module.PROXY);
IOpsProxyFactory opsProxyFactory = ProxyModule(proxyModuleAddress).opsProxyFactory();
(gelatoDedicatedCaller,) = opsProxyFactory.getProxyOf(address(this));
}

/// @notice Subscribe to the self repaying ENS renewals service for `name`.
Expand Down Expand Up @@ -116,8 +126,9 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {
// If `msg.sender` doesn't have a taskId it means it's their first time subscribing.
if (_taskIds[msg.sender] == 0) {
// We choose to pay Gelato when executing the task.
_taskIds[msg.sender] =
gelatoAutomate.createTask(address(this), abi.encode(this.renew.selector), _getModuleData(msg.sender), ETH);
_taskIds[msg.sender] = gelatoAutomate.createTask(
address(this), abi.encode(this.renew.selector), _getModuleData(msg.sender), ETH
);
}

// Add `name` to `subscriber`'s names to renew.
Expand Down Expand Up @@ -164,7 +175,7 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {
uint256 highestLimit;
string memory mostExpensiveNameToRenew;

// ⚠️ The loop is unbounded but we access each element from storage to avoid the in memory copy of the entire array.
// ⚠️ The loop is unbounded but we access each element from storage to avoid the in memory copy of the entire array.
for (uint256 i; i < len; i++) {
string memory name = subNames.at(i);
// Try to limit the renew transaction gas price which means limiting the gelato fee.
Expand Down Expand Up @@ -202,7 +213,7 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {

// Get `name` rent price.
uint256 namePrice = controller.rentPrice(name, renewalDuration);
// Get the gelato fee in ETH.
// Get the gelato fee in ETH. If this function is called by someone else than Gelato, `gelatoFee` will equal 0.
(uint256 gelatoFee,) = gelatoAutomate.getFeeDetails();
// The amount of ETH needed to pay the ENS renewal using Gelato.
uint256 neededETH = namePrice + gelatoFee;
Expand All @@ -213,9 +224,9 @@ contract SelfRepayingENS is SelfRepayingETH, Multicall {
// Renew `name` for its expiry data + `renewalDuration` first.
controller.renew{value: namePrice}(name, renewalDuration);

// Pay the Gelato executor with all the ETH left. No ETH will be stuck in this contract.
// Pay the Gelato contract with all the ETH left. No ETH will be stuck in this contract.
// Do not pay Gelato if `renew()` was called by someone else.
if (msg.sender != address(gelatoAutomate)) return;
if (msg.sender != gelatoDedicatedCaller) return;
(bool success,) = gelato.call{value: address(this).balance}("");
if (!success) revert FailedTransfer();
}
Expand Down
41 changes: 24 additions & 17 deletions test/TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {Test} from "../lib/forge-std/src/Test.sol";

import {WETHGateway} from "../lib/alchemix/src/WETHGateway.sol";
import {Whitelist} from "../lib/alchemix/src/utils/Whitelist.sol";
// TODO: This is ugly but it avoids some fake type error.
import {IAlchemistV2State} from "../lib/self-repaying-eth/lib/alchemix/src/interfaces/alchemist/IAlchemistV2State.sol";

import {Toolbox, ToolboxLocal} from "../script/ToolboxLocal.s.sol";

Expand Down Expand Up @@ -53,20 +55,16 @@ contract TestBase is Test {
vm.label(scoopy, "scoopy");
vm.deal(scoopy, 100 ether);

// Act as Scoopy, an EOA. Alchemix checks msg.sender === tx.origin to know if sender is an EOA.
vm.startPrank(scoopy, scoopy);

// Get the first supported yield ETH token.
address[] memory supportedTokens = config.alchemist.getSupportedYieldTokens();
// Create an Alchemix account.
config.wethGateway.depositUnderlying{value: 10 ether}(
address(config.alchemist), supportedTokens[0], 10 ether, scoopy, 1
);
// Create an Alchemix account with the first supported yield ETH token available.
_createAlchemixAccount(scoopy, 10 ether);

// Check `name`'s rent price.
uint256 registrationDuration = config.controller.MIN_REGISTRATION_DURATION();
uint256 namePrice = config.controller.rentPrice(name, registrationDuration);

// Act as Scoopy, an EOA. Alchemix checks msg.sender === tx.origin to know if sender is an EOA.
vm.startPrank(scoopy, scoopy);

// Register `name`.
// To register a ENS name we must first send a commitment, wait some time then register it.
// Get the commitment.
Expand All @@ -84,6 +82,22 @@ contract TestBase is Test {
vm.stopPrank();
}

/// @dev Create an Alchemix account with the first supported yield ETH token.
function _createAlchemixAccount(address target, uint256 value) internal {
address[] memory supportedTokens = config.alchemist.getSupportedYieldTokens();
address yieldToken = supportedTokens[0];

IAlchemistV2State.YieldTokenParams memory params = config.alchemist.getYieldTokenParameters(yieldToken);
assertTrue(params.enabled, "Should be enabled");
vm.prank(config.alchemist.admin());
config.alchemist.setMaximumExpectedValue(yieldToken, params.maximumExpectedValue + value);

// Act as `target`, an EOA. Alchemix checks msg.sender === tx.origin to know if sender is an EOA.
vm.prank(target, target);
// Create an Alchemix account with the first supported yield ETH token.
config.wethGateway.depositUnderlying{value: value}(address(config.alchemist), yieldToken, value, target, 1);
}

/// @dev Simulate a Gelato IAutomate call with fees.
function execRenewTask(uint256 fee, string memory _name, address subscriber) internal {
LibDataTypes.ModuleData memory moduleData = toolbox.getModuleData(srens, subscriber);
Expand All @@ -93,14 +107,7 @@ contract TestBase is Test {

// Execute the renew Gelato Task for `fee`.
config.gelatoAutomate.exec(
address(srens),
address(srens),
abi.encodeCall(srens.renew, (_name, subscriber)),
moduleData,
fee,
ETH,
false,
true
address(srens), address(srens), abi.encodeCall(srens.renew, (_name, subscriber)), moduleData, fee, ETH, true
);
}
}
8 changes: 2 additions & 6 deletions test/e2e/AttackScenarios.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ contract AttackScenarioTests is TestBase {
vm.warp(expiresAt + 1 days);

// Deploy the Freeloader contract to use `scoopy`'s account to renew `otherName`.
Freeloader freeloader = new Freeloader(
srens,
config.gelatoAutomate
);
Freeloader freeloader = new Freeloader(srens, config.gelatoAutomate);

freeloader.subscribe(otherName, scoopy);
vm.stopPrank();
Expand All @@ -54,15 +51,14 @@ contract AttackScenarioTests is TestBase {
LibDataTypes.ModuleData memory moduleData = freeloader._getModuleData(scoopy, otherName);
vm.prank(config.gelatoAutomate.gelato());
// It should not be possible !
vm.expectRevert("Ops.exec: NoErrorSelector");
vm.expectRevert("Automate.exec: OpsProxy.executeCall: NoErrorSelector");
config.gelatoAutomate.exec(
address(freeloader),
address(srens),
abi.encodeCall(srens.renew, (otherName, scoopy)),
moduleData,
gelatoFee,
ETH,
false,
true
);
}
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/Freeloader.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contract Freeloader {
view
returns (LibDataTypes.ModuleData memory moduleData)
{
moduleData = LibDataTypes.ModuleData({modules: new LibDataTypes.Module[](1), args: new bytes[](1)});
moduleData = LibDataTypes.ModuleData({modules: new LibDataTypes.Module[](2), args: new bytes[](2)});

moduleData.modules[0] = LibDataTypes.Module.RESOLVER;
moduleData.modules[1] = LibDataTypes.Module.PROXY;
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/RenewalScenarios.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ contract RenewalScenarioTests is TestBase {
///
/// @dev **_NOTE:_** It is pretty difficult to perfectly test complex protocols locally when they rely on bots as they usually don't give integrators test mocks.
/// @dev **_NOTE:_** In the following tests we won't care about Alchemix/Yearn bots and we manually simulate Gelato's.
function testFork_renewalScenario() external {
function testFork_renewalScenario_happyPath() external {
// Act as scoopy, an EOA.
vm.startPrank(scoopy, scoopy);

Expand Down
2 changes: 1 addition & 1 deletion test/integration/Checker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract CheckerTests is TestBase {

contract CheckerFailureTests is TestBase {
/// @dev Test `srens.checker()`'s returns false when there is no name to renew.
function testFork_checker_failIfNoNameToRenew() external {
function testFork_checker_failIfNoNameToRenew() external view {
(bool canExec, bytes memory execPayload) = srens.checker(scoopy);
assertFalse(canExec, "checker should return false as there is no name to renew");
assertEq(execPayload, bytes("no names to renew"), "checker should return false as there is no name to renew");
Expand Down
7 changes: 1 addition & 6 deletions test/unit/GasPriceLimit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ contract GetVariableMaxGasPriceTests is TestBase {
super.setUp();

srensHarness = new SelfRepayingENSHarness(
config.controller,
config.registrar,
config.gelatoAutomate,
config.alchemist,
config.alETHPool,
config.curveCalc
config.controller, config.registrar, config.gelatoAutomate, config.alchemist, config.alETHPool, config.weth
);
}

Expand Down
6 changes: 3 additions & 3 deletions test/unit/harnesses/SelfRepayingENSHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ contract SelfRepayingENSHarness is SelfRepayingENS {
BaseRegistrarImplementation _registrar,
Automate _gelatoAutomate,
IAlchemistV2 _alchemist,
ICurvePool _alETHPool,
ICurveCalc _curveCalc
) SelfRepayingENS(_controller, _registrar, _gelatoAutomate, _alchemist, _alETHPool, _curveCalc) {}
ICurveStableSwapNG _alETHPool,
IWETH9 _weth
) SelfRepayingENS(_controller, _registrar, _gelatoAutomate, _alchemist, _alETHPool, _weth) {}

/* --- EXPOSED INTERNAL FUNCTIONS --- */

Expand Down

0 comments on commit a7eeb93

Please sign in to comment.