Address Contract Verified
Address
0x87A07ABF94e1aB709c2e5c3ed4A1FE76901f4593
Balance
0 ETH
Nonce
1
Code Size
5688 bytes
Creator
0x256D2bB5...ff77 at tx 0x123e4645...7d426a
Indexed Transactions
0
Contract Bytecode
5688 bytes
0x608060405234801561001057600080fd5b506004361061018d5760003560e01c80637c4acabf116100e3578063bd13a8031161008c578063d8f4b6fd11610066578063d8f4b6fd14610395578063f04dba60146103bc578063f7c618c1146103c557600080fd5b8063bd13a8031461035c578063c37795c31461036f578063d547741f1461038257600080fd5b8063a110b93f116100bd578063a110b93f14610321578063a217fddf14610341578063ae0b51df1461034957600080fd5b80637c4acabf146102a657806391d14854146102cd578063941792811461031157600080fd5b80632f2ff15d11610145578063638bbc8d1161011f578063638bbc8d1461026457806363a94cf11461026c57806375b238fc1461027f57600080fd5b80632f2ff15d1461021157806336568abe146102245780635c07c5be1461023757600080fd5b8063131f957f11610176578063131f957f146101d0578063217863b7146101e5578063248a9ca3146101ee57600080fd5b806301ffc9a7146101925780630a200965146101ba575b600080fd5b6101a56101a03660046111b7565b610416565b60405190151581526020015b60405180910390f35b6101c26104af565b6040519081526020016101b1565b6101e36101de366004611275565b6104d1565b005b6101c260015481565b6101c26101fc3660046112e8565b60009081526020819052604090206001015490565b6101e361021f366004611301565b6104e5565b6101e3610232366004611301565b610510565b60035461024f90640100000000900463ffffffff1681565b60405163ffffffff90911681526020016101b1565b6101e361056e565b6101e361027a366004611331565b610617565b6101c27fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b6101c27fce3f34913921da558f105cefb578d87278debbbd073a8d552b5de0d168deee3081565b6101a56102db366004611301565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b60035461024f9063ffffffff1681565b6101c261032f366004611357565b60046020526000908152604090205481565b6101c2600081565b6101e3610357366004611374565b61070e565b6101e361036a366004611406565b6108a2565b6101e361037d3660046112e8565b610945565b6101e3610390366004611301565b610ae7565b6101c27f0ac90c257048ef1c3e387c26d4a99bde06894efbcbff862dc1885c3a9319308a81565b6101c260025481565b6003546103f19068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101b1565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806104a957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6000806104ba610b0c565b9050806104c9576001546104cb565b805b91505090565b6104de8585858585610b4e565b5050505050565b60008281526020819052604090206001015461050081610c19565b61050a8383610c26565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8116331461055f576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105698282610d22565b505050565b7fce3f34913921da558f105cefb578d87278debbbd073a8d552b5de0d168deee3061059881610c19565b600154806105d1576040517e219cf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025480156105e05760006002555b60006001819055604051829184917fd41c9e9189d59747f4e6a2d57d376e90065ef8955d80d8fb2e2499899ea4c43a9190a3505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561064181610c19565b60035463ffffffff640100000000909104811690831681900361068f576040517e219cf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1664010000000063ffffffff8681169182029290921790925560408051918416825260208201929092527f173400691a7a35b551ec31d14be2fb9e27c3ebb4bf8020f4626ca2de43db5e92910160405180910390a1505050565b6000610718610b0c565b9050801561075c57600180549082905560006002819055604051839183917ff0d86bc68879fbb68374efd811501406697382c83bf82d1f5b1c92890e2b75929190a3505b61076a338560008686610b4e565b336000908152600460205260409020548085116107b3576040517fb8d485a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107bf828761153d565b90508087111561080a576040517f66cb8b1400000000000000000000000000000000000000000000000000000000815260048101889052602481018290526044015b60405180910390fd5b86600003610816578096505b6108208783611550565b33600081815260046020526040902091909155600354610864916801000000000000000090910473ffffffffffffffffffffffffffffffffffffffff169089610ddd565b60405187815233907ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe9060200160405180910390a250505050505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217756108cc81610c19565b3360005b855181101561093d57610935828686848181106108ef576108ef611563565b9050602002013588848151811061090857610908611563565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16610ddd9092919063ffffffff16565b6001016108d0565b505050505050565b7f0ac90c257048ef1c3e387c26d4a99bde06894efbcbff862dc1885c3a9319308a61096f81610c19565b6000610979610b0c565b905080156109bd57600180549082905560006002819055604051839183917ff0d86bc68879fbb68374efd811501406697382c83bf82d1f5b1c92890e2b75929190a3505b6001548381036109f8576040517e219cf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600354640100000000900463ffffffff1615610aaf5750600254838103610a4a576040517e219cf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000164263ffffffff16179055600284905560405184907febe083bc15e066be43798b2f92026317d9a9b9c8f0ccb0b206bcd847bec596f290600090a261050a565b6001849055604051849082907ff0d86bc68879fbb68374efd811501406697382c83bf82d1f5b1c92890e2b759290600090a350505050565b600082815260208190526040902060010154610b0281610c19565b61050a8383610d22565b6002546000908015801590610b435750600354610b399063ffffffff640100000000820481169116611592565b63ffffffff164210155b6104c95760006104cb565b610be3828285610b6057600154610b64565b6002545b6040805173ffffffffffffffffffffffffffffffffffffffff8b166020820152908101899052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301520160405160208183030381529060405280519060200120610e6a565b6104de576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c238133610e82565b50565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16610d1a5760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610cb83390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104a9565b5060006104a9565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615610d1a5760008381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104a9565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610569908490610f0c565b600082610e78868685610fa2565b1495945050505050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610f08576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610801565b5050565b6000610f2e73ffffffffffffffffffffffffffffffffffffffff841683610fe6565b90508051600014158015610f53575080806020019051810190610f5191906115b6565b155b15610569576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610801565b600081815b84811015610fdb57610fd182878784818110610fc557610fc5611563565b90506020020135610ff4565b9150600101610fa7565b5090505b9392505050565b6060610fdf83836000611023565b6000818310611010576000828152602084905260409020610fdf565b6000838152602083905260409020610fdf565b606081471015611061576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610801565b6000808573ffffffffffffffffffffffffffffffffffffffff16848660405161108a91906115d3565b60006040518083038185875af1925050503d80600081146110c7576040519150601f19603f3d011682016040523d82523d6000602084013e6110cc565b606091505b50915091506110dc8683836110e6565b9695505050505050565b6060826110fb576110f682611175565b610fdf565b815115801561111f575073ffffffffffffffffffffffffffffffffffffffff84163b155b1561116e576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401610801565b5080610fdf565b8051156111855780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156111c957600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610fdf57600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610c2357600080fd5b8015158114610c2357600080fd5b60008083601f84011261123b57600080fd5b50813567ffffffffffffffff81111561125357600080fd5b6020830191508360208260051b850101111561126e57600080fd5b9250929050565b60008060008060006080868803121561128d57600080fd5b8535611298816111f9565b94506020860135935060408601356112af8161121b565b9250606086013567ffffffffffffffff8111156112cb57600080fd5b6112d788828901611229565b969995985093965092949392505050565b6000602082840312156112fa57600080fd5b5035919050565b6000806040838503121561131457600080fd5b823591506020830135611326816111f9565b809150509250929050565b60006020828403121561134357600080fd5b813563ffffffff81168114610fdf57600080fd5b60006020828403121561136957600080fd5b8135610fdf816111f9565b6000806000806060858703121561138a57600080fd5b8435935060208501359250604085013567ffffffffffffffff8111156113af57600080fd5b6113bb87828801611229565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b8035611401816111f9565b919050565b60008060006040848603121561141b57600080fd5b833567ffffffffffffffff8082111561143357600080fd5b818601915086601f83011261144757600080fd5b813560208282111561145b5761145b6113c7565b8160051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110868211171561149e5761149e6113c7565b6040529283528481018201928281018b8511156114ba57600080fd5b958301955b848710156114dd576114d0876113f6565b81529583019583016114bf565b50975050870135925050808211156114f457600080fd5b5061150186828701611229565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156104a9576104a961150e565b808201808211156104a9576104a961150e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b63ffffffff8181168382160190808211156115af576115af61150e565b5092915050565b6000602082840312156115c857600080fd5b8151610fdf8161121b565b6000825160005b818110156115f457602081860181015185830152016115da565b50600092019182525091905056fea26469706673582212201c676f3c0b88be6c6e5ea3ec09d3a8e4337963e6c41c8b2c7347fdfaeca7ce8364736f6c63430008190033
Verified Source Code Full Match
Compiler: v0.8.25+commit.b61c2a91
EVM: paris
Optimization: Yes (999999 runs)
Reward.sol 349 lines
// SPDX-License-Identifier: ISC pragma solidity 0.8.25; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @notice A rewards contract that allows a rewarder account to reward many different accounts efficiently. The rewarder * may modify reward amounts at any time to add additional rewards or nullify unclaimed rewards, for instance. * * @dev Rewards are represented as a Merkle root with claim proofs made available off-chain. A pending rewards root * delay may be specified to make it so newly published rewards roots do not take effect for some period of time. This * approach enables automated root publishing via a hot wallet, lessening the impact of wallet compromise and/or * incorrect root publishing by allowing issues to be detected and addressed before new Merkle roots take effect. * * @custom:security-contact [email protected] */ contract Reward is AccessControl { using SafeERC20 for IERC20; /*************** * ERROR TYPES * ***************/ error ClaimAmountTooBig(uint256 _requested, uint256 _availableForClaim); error InvalidProof(); error NoClaimableTokens(); error NoOp(); /********** * EVENTS * **********/ event RewardsClaimed(address indexed byAccount, uint256 amount); event PendingRewardsRootUpdated(bytes32 indexed pendingRoot); event PendingRewardsDelayUpdated(uint32 oldDelaySeconds, uint32 newDelaySeconds); event RewardsRootUpdated(bytes32 indexed oldRoot, bytes32 indexed newRoot); event RewardsRootRevoked(bytes32 indexed oldRoot, bytes32 indexed oldPendingRoot); /****************** * CONTRACT STATE * ******************/ /*** Role definitions for RBAC ***/ /// Admin role able to administer other roles and perform admin functions. bytes32 public constant ADMIN_ROLE = 0xa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775; // keccak256("ADMIN_ROLE") /// Publisher role able to publish new `rewardsRoots`, subject to a time delay. bytes32 public constant PUBLISHER_ROLE = 0x0ac90c257048ef1c3e387c26d4a99bde06894efbcbff862dc1885c3a9319308a; // keccak256("PUBLISHER_ROLE") /// Revoker role able to revoke pending and/or live `rewardsRoots` immediately. bytes32 public constant REVOKER_ROLE = 0xce3f34913921da558f105cefb578d87278debbbd073a8d552b5de0d168deee30; // keccak256("REVOKER_ROLE") /// The merkle root of each account's cumulative reward balance since the beginning of time. /// Note: No assumptions should be made about this root. The owners of this contract may update it at any time. /// NB: leaves in this tree are of the format `abi.encode(address _address, uint256 _balance)`. bytes32 public rewardsRoot; /// The pending rewards root that will take effect after `pendingRootDelay`, if set. bytes32 public pendingRewardsRoot; /// The block timestamp at which the `pendingRewardsRoot` was set. uint32 public pendingRootSetTimestamp; /// The pending rewards root delay dictating how long a new `rewardsRoot` must be pending before taking effect. uint32 public pendingRootDelaySeconds; /// The token being rewarded via this contract. IERC20 public rewardToken; /// account address => amount that has been claimed by each account. mapping(address => uint256) public totalClaimedRewards; /************* * MODIFIERS * *************/ /// Processes the `pendingRewardsRoot`, if there is one, turning it into the `rewardsRoot` if enough time has passed. modifier withPendingRootProcessed() { bytes32 vested = getVestedPendingRewardsRoot(); if (vested != bytes32(0)) { bytes32 previousRoot = rewardsRoot; rewardsRoot = vested; pendingRewardsRoot = bytes32(0); emit RewardsRootUpdated(previousRoot, vested); } _; } /**************** * PUBLIC VIEWS * ****************/ /** * @notice Gets the `rewardsRoot`, as it would be set for a claim right now. * This is useful because the contract state may contain stale data that will be updated next transaction. For * instance, the stored `rewardsRoot` will be replaced before a claim if there is a vested `pendingRewardsRoot`. * * @return The `rewardsRoot` that would be used if this were a write operation. */ function getEffectiveRewardsRoot() public view returns (bytes32) { bytes32 vested = getVestedPendingRewardsRoot(); return vested != bytes32(0) ? vested : rewardsRoot; } /** * @notice Verifies the provided address has the provided rewards balance according to the provided merkle proof, * reverting if it does not. * @param _address The address in question. * @param _rewardsBalance The rewards balance being proven. * @param _forPendingRoot True if the validation should be done against the `pendingRewardsRoot` (default is `rewardsRoot`). * @param _proof The merkle proof that the address has the balance. */ function verifyRewardsBalanceOrRevert( address _address, uint256 _rewardsBalance, bool _forPendingRoot, bytes32[] calldata _proof ) external view { _verifyRewardsBalanceOrRevert(_address, _rewardsBalance, _forPendingRoot, _proof); } /***************************** * STATE-MODIFYING FUNCTIONS * *****************************/ /** * @notice Constructs an instance of the Rewards contract to make _token claimable by rewarded accounts. Rewarded * accounts and amounts are specified in the leaf data of the Merkle Tree specified by `_initialRewardsRoot`. * * @param _admin The initial admin of this contract, which can add addresses to any role. * @param _publisher The initial address that is allowed to publish rewards roots (address(0) means nobody will have this role). * @param _revoker The initial address that is allowed to revoke rewards roots (address(0) means nobody will have this role). * @param _rewardToken The ERC20 token in which rewards will be granted. * @param _initialRewardsRoot The initial Merkle Root specifying rewards balances by account. * @param _pendingRootDelaySeconds The number of seconds after a new `rewardsRoot` is published that it takes effect. If 0, it will take effect immediately. */ constructor( address _admin, address _publisher, address _revoker, IERC20 _rewardToken, bytes32 _initialRewardsRoot, uint32 _pendingRootDelaySeconds ) { if (address(_rewardToken) == address(0)) revert(); if (_admin == address(0)) revert(); pendingRootDelaySeconds = _pendingRootDelaySeconds; rewardToken = _rewardToken; if (_initialRewardsRoot != bytes32(0)) { // NB: there is no delay for the initial root because a delay may be // enforced by not funding the contract or making Merkle proofs available. rewardsRoot = _initialRewardsRoot; } _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _grantRole(ADMIN_ROLE, _admin); _setRoleAdmin(PUBLISHER_ROLE, ADMIN_ROLE); if (_publisher != address(0)) { _grantRole(PUBLISHER_ROLE, _publisher); } _setRoleAdmin(REVOKER_ROLE, ADMIN_ROLE); if (_revoker != address(0)) { _grantRole(REVOKER_ROLE, _revoker); } } /** * @notice Claims the provided amount to the sender, assuming that address has a sufficient proven claimable amount. * * Note: If there is a vested pending root, it will be processed ahead of this call. * * @param _amountToClaim The amount to be sent to the msg.sender. If 0, all claimable rewards will be claimed. * @param _amountInProof The total reward amount in the proof that will be validated by the rewardsRoot. * @param _proof The merkle proof that proves the rewards balance for the address. */ function claim( uint256 _amountToClaim, uint256 _amountInProof, bytes32[] calldata _proof ) external withPendingRootProcessed { _verifyRewardsBalanceOrRevert(msg.sender, _amountInProof, false, _proof); uint256 alreadyClaimed = totalClaimedRewards[msg.sender]; if (_amountInProof <= alreadyClaimed) revert NoClaimableTokens(); uint256 claimableBalance = _amountInProof - alreadyClaimed; if (_amountToClaim > claimableBalance) revert ClaimAmountTooBig(_amountToClaim, claimableBalance); if (_amountToClaim == 0) { _amountToClaim = claimableBalance; } totalClaimedRewards[msg.sender] = alreadyClaimed + _amountToClaim; rewardToken.safeTransfer(msg.sender, _amountToClaim); emit RewardsClaimed(msg.sender, _amountToClaim); } /************************ * PRIVILEGED FUNCTIONS * ************************/ /** * @notice Updates the delay after which the `pendingRewardsRoot` will become the `rewardsRoot`. * * @dev This may only be called by addresses with the `ADMIN_ROLE`. * * @param _newDelaySeconds The new delay in seconds. */ function updatePendingRootDelaySeconds(uint32 _newDelaySeconds) external onlyRole(ADMIN_ROLE) { uint32 oldDelaySeconds = pendingRootDelaySeconds; if (_newDelaySeconds == oldDelaySeconds) revert NoOp(); pendingRootDelaySeconds = _newDelaySeconds; emit PendingRewardsDelayUpdated(oldDelaySeconds, _newDelaySeconds); } /** * @notice Publishes the reward Merkle Root, making new balances available for rewarded accounts after * `pendingRootDelaySeconds`, if set, immediately if not. * * Note: the leaf data in this root is cumulative. If the reward balance for an account was 100, and the owner would * like to make an additional 200 claimable, the new leaf for this account should be 300, regardless of whether the * account address has claimed the initial 100. * * Note: this is a completely trusted function. The publisher role may update the root to any value at any time. * The owner must also make proofs for this new root available offline, if there are any. * * Alternatively, the publisher may revoke rewards that have not yet been claimed by decreasing the leaf balance and/or * publishing a root of all zeros. There is a race condition with this approach in that the owner does not know * whether the rewards will be claimed prior to them submitting the updated root and it taking effect. If claimed, * the `claimedRewards` balance for the account will be increased, and future rewards will not be claimable until * they exceed the claimed amount. So in the long-run, this decrease will be enforced. * * @dev Publishing an incorrect root along with incorrect proofs may lead to all tokens held by this contract being * withdrawn by accounts with inflated incorrect merkle tree leaf data. Care should be taken in calculating and * validating leaf data against the token amount held in this contract and considering and properly setting a * `pendingRootDelaySeconds` value. * * @dev This may only be called by addresses with the `PUBLISHER_ROLE`. * * @param _newRoot The new rewards Merkle Root that dictates cumulative reward amount by account. */ function publishRewardsRoot(bytes32 _newRoot) external onlyRole(PUBLISHER_ROLE) withPendingRootProcessed { bytes32 existingRoot = rewardsRoot; if (existingRoot == _newRoot) revert NoOp(); if (pendingRootDelaySeconds > 0) { existingRoot = pendingRewardsRoot; if (existingRoot == _newRoot) revert NoOp(); pendingRootSetTimestamp = uint32(block.timestamp); pendingRewardsRoot = _newRoot; emit PendingRewardsRootUpdated(_newRoot); } else { rewardsRoot = _newRoot; emit RewardsRootUpdated(existingRoot, _newRoot); } } /** * @notice Revokes the existing `rewardsRoot` and `pendingRewardsRoot` if they exist, setting them to bytes32(0). * * @dev This may only be called by addresses with the `REVOKER_ROLE`. */ function revokeRewardsRoot() external onlyRole(REVOKER_ROLE) { bytes32 existingRoot = rewardsRoot; if (existingRoot == bytes32(0)) revert NoOp(); bytes32 existingPendingRoot = pendingRewardsRoot; if (existingPendingRoot != bytes32(0)) { pendingRewardsRoot = bytes32(0); } rewardsRoot = bytes32(0); emit RewardsRootRevoked(existingRoot, existingPendingRoot); } /** * @notice This function allows admins to withdraw ERC-20 tokens. While this contract should only ever hold the * `rewardToken`, other tokens may be sent to it by accident or on purpose. This function allows any ERC-20 token * held by this contract to be withdrawn. * * @dev This may only be called by addresses with the `ADMIN_ROLE`. * * @param _tokens The array of tokens to be withdrawn. Note: indexes in this array correspond to those in `_amounts`. * @param _amounts The array of amounts to be withdrawn. Note: indexes in this array correspond to those in `_tokens`. */ function withdrawTokens(IERC20[] memory _tokens, uint256[] calldata _amounts) external onlyRole(ADMIN_ROLE) { address admin = msg.sender; for (uint256 i = 0; i < _tokens.length; i++) { _tokens[i].safeTransfer(admin, _amounts[i]); } } /******************************** * PRIVATE / INTERNAL FUNCTIONS * ********************************/ /** * @notice Verifies the provided address has the provided rewards balance according to the provided merkle proof, * reverting if it does not. * @param _address The address in question. * @param _rewardsBalance The rewards balance being proven. * @param _forPendingRoot True if the validation should be done against the `pendingRewardsRoot` (default is `rewardsRoot`). * @param _proof The merkle proof that the address has the balance. */ function _verifyRewardsBalanceOrRevert( address _address, uint256 _rewardsBalance, bool _forPendingRoot, bytes32[] calldata _proof ) private view { if ( !MerkleProof.verifyCalldata( _proof, _forPendingRoot ? pendingRewardsRoot : rewardsRoot, keccak256(abi.encodePacked(keccak256(abi.encode(_address, _rewardsBalance)))) ) ) { revert InvalidProof(); } } /** * @notice Returns the `pendingRewardsRoot` if it is set and has vested for pendingRootDelaySeconds time. * * @return The vested pending rewards root, if there is one, bytes32(0) otherwise. */ function getVestedPendingRewardsRoot() internal view returns (bytes32) { bytes32 root = pendingRewardsRoot; return root != bytes32(0) && block.timestamp >= pendingRootSetTimestamp + pendingRootDelaySeconds ? root : bytes32(0); } }
AccessControl.sol 209 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
MerkleProof.sol 232 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
ERC165.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Read Contract
ADMIN_ROLE 0x75b238fc → bytes32
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
PUBLISHER_ROLE 0xd8f4b6fd → bytes32
REVOKER_ROLE 0x7c4acabf → bytes32
getEffectiveRewardsRoot 0x0a200965 → bytes32
getRoleAdmin 0x248a9ca3 → bytes32
hasRole 0x91d14854 → bool
pendingRewardsRoot 0xf04dba60 → bytes32
pendingRootDelaySeconds 0x5c07c5be → uint32
pendingRootSetTimestamp 0x94179281 → uint32
rewardToken 0xf7c618c1 → address
rewardsRoot 0x217863b7 → bytes32
supportsInterface 0x01ffc9a7 → bool
totalClaimedRewards 0xa110b93f → uint256
verifyRewardsBalanceOrRevert 0x131f957f
Write Contract 8 functions
These functions modify contract state and require a wallet transaction to execute.
claim 0xae0b51df
uint256 _amountToClaim
uint256 _amountInProof
bytes32[] _proof
grantRole 0x2f2ff15d
bytes32 role
address account
publishRewardsRoot 0xc37795c3
bytes32 _newRoot
renounceRole 0x36568abe
bytes32 role
address callerConfirmation
revokeRewardsRoot 0x638bbc8d
No parameters
revokeRole 0xd547741f
bytes32 role
address account
updatePendingRootDelaySeconds 0x63a94cf1
uint32 _newDelaySeconds
withdrawTokens 0xbd13a803
address[] _tokens
uint256[] _amounts
Recent Transactions
No transactions found for this address