-
Notifications
You must be signed in to change notification settings - Fork 2
feat: prices automation support for chainlink v2.3 #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7fa70d1
ca1b0bb
4c9b9c8
6df5b96
6a99359
b73542b
ef746ce
0244808
6d71bec
9fe59c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,26 +5,20 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | |
| import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
| import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
| import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
| import {StableEnumerableSet} from "./libraries/StableEnumerableSet.sol"; | ||
| import {Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Can we move the |
||
| import {IVoter} from "../../vendor/velodrome-contracts/contracts/interfaces/IVoter.sol"; | ||
| import {IKeeperRegistryMaster} from "@chainlink/contracts/src/v0.8/automation/interfaces/v2_1/IKeeperRegistryMaster.sol"; | ||
| import {IAutomationRegistrarV2_1} from "../interfaces/v2_1/IAutomationRegistrarV2_1.sol"; | ||
| import {IUpkeepBalanceMonitor} from "../interfaces/IUpkeepBalanceMonitor.sol"; | ||
| import {IPrices} from "./interfaces/IPrices.sol"; | ||
| import {TokenUpkeep} from "./TokenUpkeep.sol"; | ||
| import {ITokenUpkeepManager} from "./interfaces/ITokenUpkeepManager.sol"; | ||
|
|
||
| contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | ||
| import {StableEnumerableSet} from "../libraries/StableEnumerableSet.sol"; | ||
| import {IVoter} from "../../../vendor/velodrome-contracts/contracts/interfaces/IVoter.sol"; | ||
| import {IPrices} from "../interfaces/IPrices.sol"; | ||
| import {ITokenUpkeepManager} from "../interfaces/common/ITokenUpkeepManager.sol"; | ||
|
|
||
| abstract contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | ||
| using SafeERC20 for IERC20; | ||
| using StableEnumerableSet for StableEnumerableSet.AddressSet; | ||
| using EnumerableSet for EnumerableSet.UintSet; | ||
|
|
||
| /// @inheritdoc ITokenUpkeepManager | ||
| address public immutable override linkToken; | ||
| /// @inheritdoc ITokenUpkeepManager | ||
| address public immutable override keeperRegistry; | ||
| /// @inheritdoc ITokenUpkeepManager | ||
| address public immutable override automationRegistrar; | ||
| /// @inheritdoc ITokenUpkeepManager | ||
| address public immutable override voter; | ||
|
|
@@ -50,18 +44,17 @@ contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | |
| uint256[] public override upkeepIds; | ||
|
|
||
| StableEnumerableSet.AddressSet internal _tokenList; | ||
| EnumerableSet.UintSet private _cancelledUpkeepIds; | ||
| EnumerableSet.UintSet internal _cancelledUpkeepIds; | ||
|
|
||
| uint256 private constant TOKENS_PER_UPKEEP = 100; | ||
| uint256 internal constant TOKENS_PER_UPKEEP = 100; | ||
| uint8 internal constant CONDITIONAL_TRIGGER_TYPE = 0; | ||
| string internal constant UPKEEP_NAME = "Token upkeep"; | ||
| uint256 private constant UPKEEP_CANCEL_BUFFER = 20; | ||
| uint8 private constant CONDITIONAL_TRIGGER_TYPE = 0; | ||
| string private constant UPKEEP_NAME = "Token upkeep"; | ||
|
|
||
| bytes32 private constant WHITELIST_TOKEN_EVENT = 0x44948130cf88523dbc150908a47dd6332c33a01a3869d7f2fa78e51d5a5f9c57; | ||
|
|
||
| constructor( | ||
| address _linkToken, | ||
| address _keeperRegistry, | ||
| address _automationRegistrar, | ||
| address _voter, | ||
| address _pricesOracle, | ||
|
|
@@ -70,7 +63,6 @@ contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | |
| uint32 _newUpkeepGasLimit | ||
| ) { | ||
| linkToken = _linkToken; | ||
| keeperRegistry = _keeperRegistry; | ||
| automationRegistrar = _automationRegistrar; | ||
| voter = _voter; | ||
| pricesOracle = _pricesOracle; | ||
|
|
@@ -337,51 +329,11 @@ contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | |
| emit TokenDeregistered(_token); | ||
| } | ||
|
|
||
| function _registerTokenUpkeep() internal { | ||
| uint256 startIndex = _getNextUpkeepStartIndex(upkeepIds.length); | ||
| uint256 endIndex = startIndex + TOKENS_PER_UPKEEP; | ||
| address _tokenUpkeep = address(new TokenUpkeep(startIndex, endIndex)); | ||
| isTokenUpkeep[_tokenUpkeep] = true; | ||
| IAutomationRegistrarV2_1.RegistrationParams memory params = IAutomationRegistrarV2_1.RegistrationParams({ | ||
| name: UPKEEP_NAME, | ||
| encryptedEmail: "", | ||
| upkeepContract: _tokenUpkeep, | ||
| gasLimit: newUpkeepGasLimit, | ||
| adminAddress: address(this), | ||
| triggerType: CONDITIONAL_TRIGGER_TYPE, | ||
| checkData: "", | ||
| triggerConfig: "", | ||
| offchainConfig: "", | ||
| amount: newUpkeepFundAmount | ||
| }); | ||
| uint256 upkeepId = _registerUpkeep(params); | ||
| upkeepIds.push(upkeepId); | ||
| tokenUpkeep[upkeepId] = _tokenUpkeep; | ||
| address forwarder = IKeeperRegistryMaster(keeperRegistry).getForwarder(upkeepId); | ||
| TokenUpkeep(_tokenUpkeep).setTrustedForwarder(forwarder); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).addToWatchList(upkeepId); | ||
| emit TokenUpkeepRegistered(_tokenUpkeep, upkeepId, startIndex, endIndex); | ||
| } | ||
| function _registerTokenUpkeep() internal virtual; | ||
|
|
||
| function _registerUpkeep(IAutomationRegistrarV2_1.RegistrationParams memory _params) internal returns (uint256) { | ||
| IERC20(linkToken).safeIncreaseAllowance(automationRegistrar, _params.amount); | ||
| uint256 upkeepID = IAutomationRegistrarV2_1(automationRegistrar).registerUpkeep(_params); | ||
| if (upkeepID != 0) { | ||
| return upkeepID; | ||
| } else { | ||
| revert AutoApproveDisabled(); | ||
| } | ||
| } | ||
| function _cancelTokenUpkeep(uint256 _upkeepId) internal virtual; | ||
|
|
||
| function _cancelTokenUpkeep(uint256 _upkeepId) internal { | ||
| upkeepIds.pop(); | ||
| delete isTokenUpkeep[tokenUpkeep[_upkeepId]]; | ||
| delete tokenUpkeep[_upkeepId]; | ||
| _cancelledUpkeepIds.add(_upkeepId); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).removeFromWatchList(_upkeepId); | ||
| IKeeperRegistryMaster(keeperRegistry).cancelUpkeep(_upkeepId); | ||
| emit TokenUpkeepCancelled(_upkeepId); | ||
| } | ||
| function _withdrawUpkeep(uint256 _upkeepId) internal virtual; | ||
|
|
||
| function _finishUpkeepAndCleanup(uint256 _lastRun) internal { | ||
| finishedUpkeeps[_lastRun]++; | ||
|
|
@@ -395,12 +347,6 @@ contract TokenUpkeepManager is ITokenUpkeepManager, Ownable { | |
| emit TokenListCleaned(); | ||
| } | ||
|
|
||
| function _withdrawUpkeep(uint256 _upkeepId) internal { | ||
| _cancelledUpkeepIds.remove(_upkeepId); | ||
| IKeeperRegistryMaster(keeperRegistry).withdrawFunds(_upkeepId, address(this)); | ||
| emit TokenUpkeepWithdrawn(_upkeepId); | ||
| } | ||
|
|
||
| function _getNextUpkeepStartIndex(uint256 _upkeepCount) internal pure returns (uint256) { | ||
| return _upkeepCount * TOKENS_PER_UPKEEP; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.6; | ||
|
|
||
| import {ITokenUpkeepManager} from "../common/ITokenUpkeepManager.sol"; | ||
|
|
||
| interface ITokenUpkeepManagerV2_1 is ITokenUpkeepManager { | ||
| /// @notice Keeper registry address | ||
| function keeperRegistry() external view returns (address); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.6; | ||
|
|
||
| import {ITokenUpkeepManager} from "../common/ITokenUpkeepManager.sol"; | ||
|
|
||
| interface ITokenUpkeepManagerV2_3 is ITokenUpkeepManager { | ||
| /// @notice Keeper registry address | ||
| function keeperRegistry() external view returns (address payable); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.6; | ||
|
|
||
| import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
| import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
| import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
| import {IKeeperRegistryMaster} from "@chainlink/contracts/src/v0.8/automation/interfaces/v2_1/IKeeperRegistryMaster.sol"; | ||
| import {IAutomationRegistrarV2_1} from "../../interfaces/v2_1/IAutomationRegistrarV2_1.sol"; | ||
| import {ITokenUpkeepManagerV2_1} from "../interfaces/v2_1/ITokenUpkeepManagerV2_1.sol"; | ||
| import {IUpkeepBalanceMonitor} from "../../interfaces/IUpkeepBalanceMonitor.sol"; | ||
| import {TokenUpkeepManager} from "../common/TokenUpkeepManager.sol"; | ||
| import {TokenUpkeep} from "../common/TokenUpkeep.sol"; | ||
|
|
||
| contract TokenUpkeepManagerV2_1 is TokenUpkeepManager, ITokenUpkeepManagerV2_1 { | ||
| using SafeERC20 for IERC20; | ||
| using EnumerableSet for EnumerableSet.UintSet; | ||
|
|
||
| /// @inheritdoc ITokenUpkeepManagerV2_1 | ||
| address public immutable override keeperRegistry; | ||
|
|
||
| constructor( | ||
| address _linkToken, | ||
| address _keeperRegistry, | ||
| address _automationRegistrar, | ||
| address _voter, | ||
| address _pricesOracle, | ||
| address _upkeepBalanceMonitor, | ||
| uint96 _newUpkeepFundAmount, | ||
| uint32 _newUpkeepGasLimit | ||
| ) | ||
| TokenUpkeepManager( | ||
| _linkToken, | ||
| _automationRegistrar, | ||
| _voter, | ||
| _pricesOracle, | ||
| _upkeepBalanceMonitor, | ||
| _newUpkeepFundAmount, | ||
| _newUpkeepGasLimit | ||
| ) | ||
| { | ||
| keeperRegistry = _keeperRegistry; | ||
| } | ||
|
|
||
| function _registerTokenUpkeep() internal override { | ||
| uint256 startIndex = _getNextUpkeepStartIndex(upkeepIds.length); | ||
| uint256 endIndex = startIndex + TOKENS_PER_UPKEEP; | ||
| address _tokenUpkeep = address(new TokenUpkeep(startIndex, endIndex)); | ||
| isTokenUpkeep[_tokenUpkeep] = true; | ||
| IAutomationRegistrarV2_1.RegistrationParams memory params = IAutomationRegistrarV2_1.RegistrationParams({ | ||
| name: UPKEEP_NAME, | ||
| encryptedEmail: "", | ||
| upkeepContract: _tokenUpkeep, | ||
| gasLimit: newUpkeepGasLimit, | ||
| adminAddress: address(this), | ||
| triggerType: CONDITIONAL_TRIGGER_TYPE, | ||
| checkData: "", | ||
| triggerConfig: "", | ||
| offchainConfig: "", | ||
| amount: newUpkeepFundAmount | ||
| }); | ||
| uint256 upkeepId = _registerUpkeep(params); | ||
| upkeepIds.push(upkeepId); | ||
| tokenUpkeep[upkeepId] = _tokenUpkeep; | ||
| address forwarder = IKeeperRegistryMaster(keeperRegistry).getForwarder(upkeepId); | ||
| TokenUpkeep(_tokenUpkeep).setTrustedForwarder(forwarder); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).addToWatchList(upkeepId); | ||
| emit TokenUpkeepRegistered(_tokenUpkeep, upkeepId, startIndex, endIndex); | ||
| } | ||
|
|
||
| function _registerUpkeep(IAutomationRegistrarV2_1.RegistrationParams memory _params) internal returns (uint256) { | ||
| IERC20(linkToken).safeIncreaseAllowance(automationRegistrar, _params.amount); | ||
| uint256 upkeepID = IAutomationRegistrarV2_1(automationRegistrar).registerUpkeep(_params); | ||
| if (upkeepID != 0) { | ||
| return upkeepID; | ||
| } else { | ||
| revert AutoApproveDisabled(); | ||
| } | ||
| } | ||
|
|
||
| function _cancelTokenUpkeep(uint256 _upkeepId) internal override { | ||
| upkeepIds.pop(); | ||
| delete isTokenUpkeep[tokenUpkeep[_upkeepId]]; | ||
| delete tokenUpkeep[_upkeepId]; | ||
| _cancelledUpkeepIds.add(_upkeepId); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).removeFromWatchList(_upkeepId); | ||
| IKeeperRegistryMaster(keeperRegistry).cancelUpkeep(_upkeepId); | ||
| emit TokenUpkeepCancelled(_upkeepId); | ||
| } | ||
|
|
||
| function _withdrawUpkeep(uint256 _upkeepId) internal override { | ||
| _cancelledUpkeepIds.remove(_upkeepId); | ||
| IKeeperRegistryMaster(keeperRegistry).withdrawFunds(_upkeepId, address(this)); | ||
| emit TokenUpkeepWithdrawn(_upkeepId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.6; | ||
|
|
||
| import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
| import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
| import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
| import {IAutomationRegistryMaster2_3} from "@chainlink/contracts/src/v0.8/automation/interfaces/v2_3/IAutomationRegistryMaster2_3.sol"; | ||
| import {IAutomationRegistrarV2_3} from "../../interfaces/v2_3/IAutomationRegistrarV2_3.sol"; | ||
| import {ITokenUpkeepManagerV2_3} from "../interfaces/v2_3/ITokenUpkeepManagerV2_3.sol"; | ||
| import {IUpkeepBalanceMonitor} from "../../interfaces/IUpkeepBalanceMonitor.sol"; | ||
| import {TokenUpkeepManager} from "../common/TokenUpkeepManager.sol"; | ||
| import {TokenUpkeep} from "../common/TokenUpkeep.sol"; | ||
|
|
||
| contract TokenUpkeepManagerV2_3 is TokenUpkeepManager, ITokenUpkeepManagerV2_3 { | ||
| using SafeERC20 for IERC20; | ||
| using EnumerableSet for EnumerableSet.UintSet; | ||
|
|
||
| /// @inheritdoc ITokenUpkeepManagerV2_3 | ||
| address payable public immutable override keeperRegistry; | ||
|
|
||
| constructor( | ||
| address _linkToken, | ||
| address payable _keeperRegistry, | ||
| address _automationRegistrar, | ||
| address _voter, | ||
| address _pricesOracle, | ||
| address _upkeepBalanceMonitor, | ||
| uint96 _newUpkeepFundAmount, | ||
| uint32 _newUpkeepGasLimit | ||
| ) | ||
| TokenUpkeepManager( | ||
| _linkToken, | ||
| _automationRegistrar, | ||
| _voter, | ||
| _pricesOracle, | ||
| _upkeepBalanceMonitor, | ||
| _newUpkeepFundAmount, | ||
| _newUpkeepGasLimit | ||
| ) | ||
| { | ||
| keeperRegistry = _keeperRegistry; | ||
| } | ||
|
|
||
| function _registerTokenUpkeep() internal override { | ||
| uint256 startIndex = _getNextUpkeepStartIndex(upkeepIds.length); | ||
| uint256 endIndex = startIndex + TOKENS_PER_UPKEEP; | ||
| address _tokenUpkeep = address(new TokenUpkeep(startIndex, endIndex)); | ||
| isTokenUpkeep[_tokenUpkeep] = true; | ||
| IAutomationRegistrarV2_3.RegistrationParams memory params = IAutomationRegistrarV2_3.RegistrationParams({ | ||
| name: UPKEEP_NAME, | ||
| encryptedEmail: "", | ||
| upkeepContract: _tokenUpkeep, | ||
| gasLimit: newUpkeepGasLimit, | ||
| adminAddress: address(this), | ||
| triggerType: CONDITIONAL_TRIGGER_TYPE, | ||
| checkData: "", | ||
| triggerConfig: "", | ||
| offchainConfig: "", | ||
| amount: newUpkeepFundAmount, | ||
| billingToken: IERC20(linkToken) | ||
| }); | ||
| uint256 upkeepId = _registerUpkeep(params); | ||
| upkeepIds.push(upkeepId); | ||
| tokenUpkeep[upkeepId] = _tokenUpkeep; | ||
| address forwarder = IAutomationRegistryMaster2_3(keeperRegistry).getForwarder(upkeepId); | ||
| TokenUpkeep(_tokenUpkeep).setTrustedForwarder(forwarder); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).addToWatchList(upkeepId); | ||
| emit TokenUpkeepRegistered(_tokenUpkeep, upkeepId, startIndex, endIndex); | ||
| } | ||
|
|
||
| function _registerUpkeep(IAutomationRegistrarV2_3.RegistrationParams memory _params) internal returns (uint256) { | ||
| IERC20(linkToken).safeIncreaseAllowance(automationRegistrar, _params.amount); | ||
| uint256 upkeepID = IAutomationRegistrarV2_3(automationRegistrar).registerUpkeep(_params); | ||
| if (upkeepID != 0) { | ||
| return upkeepID; | ||
| } else { | ||
| revert AutoApproveDisabled(); | ||
| } | ||
| } | ||
|
|
||
| function _cancelTokenUpkeep(uint256 _upkeepId) internal override { | ||
| upkeepIds.pop(); | ||
| delete isTokenUpkeep[tokenUpkeep[_upkeepId]]; | ||
| delete tokenUpkeep[_upkeepId]; | ||
| _cancelledUpkeepIds.add(_upkeepId); | ||
| IUpkeepBalanceMonitor(upkeepBalanceMonitor).removeFromWatchList(_upkeepId); | ||
| IAutomationRegistryMaster2_3(keeperRegistry).cancelUpkeep(_upkeepId); | ||
| emit TokenUpkeepCancelled(_upkeepId); | ||
| } | ||
|
|
||
| function _withdrawUpkeep(uint256 _upkeepId) internal override { | ||
| _cancelledUpkeepIds.remove(_upkeepId); | ||
| IAutomationRegistryMaster2_3(keeperRegistry).withdrawFunds(_upkeepId, address(this)); | ||
| emit TokenUpkeepWithdrawn(_upkeepId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,8 @@ | |
| "test:unit": "npx hardhat test test/unit/*.test.ts test/**/unit/*.test.ts test/**/unit/**/*.test.ts", | ||
| "test:fork": "FORK_ENABLED=true FORK_CHAIN=optimism BLOCK_NUMBER=128856225 npx hardhat test test/emissions/fork/v2_1/GaugeUpkeepManagerV2_1.test.ts", | ||
| "test:fork:v2_3": "FORK_ENABLED=true FORK_CHAIN=base BLOCK_NUMBER=27557816 npx hardhat test test/emissions/fork/v2_3/GaugeUpkeepManagerV2_3.test.ts", | ||
| "test:fork:prices": "FORK_ENABLED=true FORK_CHAIN=optimism BLOCK_NUMBER=135890825 npx hardhat test test/prices/fork/TokenUpkeepManager.test.ts", | ||
| "test:fork:prices": "FORK_ENABLED=true FORK_CHAIN=optimism BLOCK_NUMBER=135890825 npx hardhat test test/prices/fork/v2_1/TokenUpkeepManagerV2_1.test.ts", | ||
| "test:fork:prices:v2_3": "FORK_ENABLED=true FORK_CHAIN=base BLOCK_NUMBER=30517684 npx hardhat test test/prices/fork/v2_3/TokenUpkeepManagerV2_3.test.ts", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will likely have some conflicts with the changes in #55, so once that's merged this file should be updated accordingly (i.e.: |
||
| "prettier:check": "prettier '**/*' --check", | ||
| "prettier:write": "prettier '**/*' --write" | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can also move the
TokenUpkeepcontract into thecommonfolder