Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xd23aa7EdF42CD3Fc4CD391faAbc0c207B1c86542
Balance 0 ETH
Nonce 1
Code Size 7631 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7631 bytes
0x60806040526004361015610018575b61001661061e565b005b60003560e01c806303aaf08b146105f757806306394c9b146105d05780630700037d146105965780630c9cbf0e1461056b5780630cc630c714610543578063101cef481461051c5780631e2720ff146104f557806333f642f6146104cb57806348a61ff51461049d5780635512b81114610473578063570ca735146104495780635b2478ea1461041f578063715018a6146103f857806374c13fda146103ce57806379ba5097146103a75780637fdba5f61461037d5780638686ebcc146103535780638da5cb5b146103295780638e3fb0d314610302578063960d264d146102db578063a230c5241461029f578063a480ca7914610278578063b688a36314610251578063bc063e1a14610216578063d379be23146101ec578063d66d9e19146101c5578063e30c3978146101805763f2fde38b146101565761000e565b34610173575b61001661016a366004610655565b610b3b565b0390f35b61017b61061e565b61015c565b50346101b8575b6101923660046107f0565b61016f61019d610a97565b6040515b918291826001600160a01b03909116815260200190565b6101c061061e565b610187565b50346101df575b6101d73660046107f0565b610016610cc4565b6101e761061e565b6101cc565b5034610209575b6101fe3660046107f0565b61016f61019d6109ab565b61021161061e565b6101f3565b5034610244575b6102283660046107f0565b61016f6102336109a3565b6040515b9182918290815260200190565b61024c61061e565b61021d565b503461026b575b6102633660046107f0565b610016610c7b565b61027361061e565b610258565b5034610292575b61001661028d366004610655565b611b10565b61029a61061e565b61027f565b50346102ce575b61016f6102bc6102b7366004610655565b610980565b60405191829182901515815260200190565b6102d661061e565b6102a6565b50346102f5575b6100166102f03660046107cf565b611c13565b6102fd61061e565b6102e2565b503461031c575b610016610317366004610655565b611b4c565b61032461061e565b610309565b5034610346575b61033b3660046107f0565b61016f61019d6109ca565b61034e61061e565b610330565b5034610370575b6103653660046107f0565b61016f61023361095f565b61037861061e565b61035a565b503461039a575b61038f3660046107f0565b61016f61023361094b565b6103a261061e565b610384565b50346103c1575b6103b93660046107f0565b610016610c34565b6103c961061e565b6103ae565b50346103eb575b6103e03660046107f0565b61016f61019d610927565b6103f361061e565b6103d5565b5034610412575b61040a3660046107f0565b610016610a0c565b61041a61061e565b6103ff565b503461043c575b61016f610233610437366004610655565b610915565b61044461061e565b610426565b5034610466575b61045b3660046107f0565b61016f61019d610909565b61046e61061e565b610450565b5034610490575b6104853660046107f0565b61016f61019d6108fd565b61049861061e565b61047a565b50346104be575b6100166104b236600461085b565b95949094939193610ecf565b6104c661061e565b6104a4565b50346104e8575b6104dd3660046107f0565b61016f610233610803565b6104f061061e565b6104d2565b503461050f575b61001661050a3660046107cf565b6118c0565b61051761061e565b6104fc565b5034610536575b610016610531366004610655565b611bf5565b61053e61061e565b610523565b503461055e575b610016610558366004610791565b91611a71565b61056661061e565b61054a565b5034610589575b61016f610233610583366004610718565b9061075f565b61059161061e565b610572565b50346105c3575b61016f6105b36105ae366004610655565b6106c5565b6040519193915b938493846106f6565b6105cb61061e565b61059d565b50346105ea575b6100166105e5366004610655565b611b2e565b6105f261061e565b6105d7565b5034610611575b61001661060c366004610655565b611b6a565b61061961061e565b6105fe565b50600080fd5b6001600160a01b031690565b90565b6001600160a01b0381165b141561061e57565b9050359061065382610633565b565b906106309160208183031261066957610646565b61067161061e565b610646565b61063090610624906001600160a01b031682565b61063090610676565b6106309061068a565b906106a690610693565b600052602052604060002090565b6106309081565b61063090546106b4565b6106d090600461069c565b6106d9816106bb565b9161063060026106eb600185016106bb565b93016106bb565b9052565b908152606081019392610653929091604091610714905b6020830152565b0152565b91906106309060408482031261073c575b6107338185610646565b93602001610646565b61074461061e565b610729565b610630916008021c81565b906106309154610749565b6107796106309261077460059360009461069c565b61069c565b610754565b8061063e565b905035906106538261077e565b90916060828403126107c2575b6106306107ab8484610646565b936107b98160208601610784565b93604001610784565b6107ca61061e565b61079e565b90610630916020818303126107e357610784565b6107eb61061e565b610784565b60009103126107fb57565b61065361061e565b6106306000600a610754565b909182601f8301121561084e575b602082359267ffffffffffffffff8411610841575b0192602083028401116107fb57565b61084961061e565b610832565b61085661061e565b61081d565b909160c0828403126108d8575b6108728383610646565b926106306108838260208601610646565b936108918360408301610784565b936108b284606084013567ffffffffffffffff81116108cb575b840161080f565b9390946108c28160808601610784565b9360a001610646565b6108d361061e565b6108ab565b6108e061061e565b610868565b610630916008021c610624565b9061063091546108e5565b610630600060096108f2565b610630600060076108f2565b6106309061077960039160009261069c565b610630600060066108f2565b6106306106306106309290565b610630612a30610933565b610630610940565b610630620f4240610933565b610630610953565b610630916008021c60ff1690565b906106309154610967565b6106309061099260029160009261069c565b610975565b610630620186a0610933565b610630610997565b610630600060086108f2565b61063090610624565b61063090546109b7565b61063060006109c0565b6109dc610a6d565b6106536109fa565b6106246106306106309290565b610630906109e4565b610653610a0760006109f1565b610b82565b6106536109d4565b0190565b15610a1f57565b5060405162461bcd60e51b815280610a69600482016020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b0390fd5b610653610a786109ca565b610a91610a8433610624565b916001600160a01b031690565b14610a18565b61063060016109c0565b61065390610aad610a6d565b610ae2565b906001600160a01b03905b9181191691161790565b90610ad7610630610ade92610693565b8254610ab2565b9055565b610aed816001610ac7565b610af56109ca565b90610b29610b237f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270093610693565b91610693565b91610b3360405190565b80805b0390a3565b61065390610aa1565b916001600160a01b0360089290920291821b911b610abd565b9190610b6e610630610ade93610693565b908354610b44565b61065391600091610b5d565b61065390610b9260006001610b76565b610b9c60006109c0565b90610ba8816000610ac7565b610b29610b237f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093610693565b15610bdc57565b5060405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b61065333610a07610c43610a97565b610c556001600160a01b038416610a84565b14610bd5565b9060ff90610abd565b90610c74610630610ade92151590565b8254610c5b565b33610c916001610c8c83600261069c565b610c64565b610cbf7f0abf3b3f643594d958297062a019458e27d7766629590ac621aa1000fa1298ab916101a160405190565b0390a1565b33610cd56000610c8c83600261069c565b610cbf7fbea911b50ccdd2233b28faa49766c3cbd0631608f32e5a724d7b83b32b681ad0916101a160405190565b50634e487b7160e01b600052601160045260246000fd5b90610d24565b9190565b908060001904821181151516610d38570290565b610d40610d03565b0290565b50634e487b7160e01b600052601260045260246000fd5b8115610d65570490565b610d6d610d44565b0490565b818110610d7c570390565b610d84610d03565b0390565b81198111610d94570190565b610a14610d03565b9060001990610abd565b90610db6610630610ade92610933565b8254610d9c565b601f01601f191690565b50634e487b7160e01b600052604160045260246000fd5b90601f01601f1916810190811067ffffffffffffffff821117610e0057604052565b610e08610dc7565b604052565b90610653610e1a60405190565b9283610dde565b6106306060610e0d565b90610653610e696002610e3c610e21565b94610e4d610e49826106bb565b8752565b610e63610e5c600183016106bb565b6020880152565b016106bb565b6040840152565b61063090610e2b565b6106f2906001600160a01b031660601b90565b601481610e9f610a149360209695610e79565b01918252565b908152606081019392610653929091604091610ec09061070d565b01906001600160a01b03169052565b93909433610ee3610a8461062460086109c0565b141561118157600091610ef8610624846109f1565b6001600160a01b0388161461116057610f1083610933565b841461113f57610f21610630610997565b821161111e57610f43610f35879386610d1a565b610f3d610953565b90610d5b565b94610f75610f518787610d71565b96610f6f610f6086600361069c565b91610f6a836106bb565b610d88565b90610da6565b600491610f8a610f85858561069c565b610e70565b94610fa787610f6a610fa28d61077460059a8b61069c565b6106bb565b9581810191610fc0610d20610fba855190565b92610933565b146110f2576040015142106110c7579161102391610fdf611027945190565b908b61100a610fed60405190565b8092610ffe8c602084019283610e8c565b90810382520382610dde565b61101c611015825190565b9160200190565b20926115bd565b1590565b61109f57509061103e86610774876110439561069c565b610da6565b611056828661105186610693565b6111e4565b610b3661108c6110867f70fec9c7f21f70099818a4619c69af3a4677031e67acea18286c0eca3a754bce95610693565b95610693565b9561109660405190565b93849384610ea5565b9650505050505050610a6991506110b560405190565b6309bde33960e01b8152918291820190565b5050509650505050505050610a6991506110e060405190565b630995309b60e01b8152918291820190565b505050509650505050505050610a69915061110c60405190565b630d16b83360e31b8152918291820190565b50505050505050505061113060405190565b6358d620b360e01b8152600490fd5b50505050505050505061115160405190565b63162908e360e11b8152600490fd5b50505050505050505061117260405190565b633b4f091f60e21b8152600490fd5b505050505050505061119260405190565b63ea8e4eb560e01b8152600490fd5b6111ba6111b46106309263ffffffff1690565b60e01b90565b6001600160e01b03191690565b6001600160a01b0390911681526040810192916106539160200152565b611227600492611218610653956111fe63a9059cbb6111a1565b9261120860405190565b96879460208601908152016111c7565b60208201810382520383610dde565b61133b565b610a1460209167ffffffffffffffff811161124b57601f01601f191690565b610dbd610dc7565b906112656112608361122c565b610e0d565b918252565b6112746020611253565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602082015290565b61063061126a565b80151561063e565b90505190610653826112a5565b90610630916020818303126112ce576112ad565b6112d661061e565b6112ad565b156112e257565b5060405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b6106539161134b61135a92610693565b9061135461129d565b91611398565b8051611369610d206000610933565b14908115611378575b506112db565b61139291506020611387825190565b8183010191016112ba565b38611372565b61063092916113a76000610933565b91611428565b156113b457565b5060405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608490fd5b3d15611423576114183d611253565b903d6000602084013e565b606090565b9060006106309493819261143a606090565b5061145161144730610693565b83903110156113ad565b60208101905191855af1611463611409565b916114b6565b1561147057565b5060405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b9192606091156114eb57505081516114d1610d206000610933565b146114da575090565b6114e6610630916114f8565b611469565b909392610653925061157a565b3b611506610d206000610933565b1190565b918091926000905b82821061152a575011611523575050565b6000910152565b91508060209183015181860152018291611512565b611560610dbd602093610a1493611554815190565b80835293849260200190565b9586910161150a565b60208082526106309291019061153f565b9150611584825190565b611591610d206000610933565b11156115a05750805190602001fd5b604051610a69925062461bcd60e51b815291829160048301611569565b6115dc9293610d20926115d8926115d2600090565b5061162e565b9290565b1490565b6001906000198114610d94570190565b50634e487b7160e01b600052603260045260246000fd5b9160209181101561161757020190565b61161f6115f0565b020190565b356106308161077e565b6116386000610933565b925b8284101561166e576116626116689161165c611657878787611607565b611624565b90611675565b936115e0565b9261163a565b9250505090565b81811015611690579061063091600052602052604060002090565b61063091600052602052604060002090565b336116b061062460076109c0565b6001600160a01b038216141590816116db575b506116d15761065390611730565b5050604051611192565b90506116ed610a8461062460096109c0565b1415386116c3565b905051906106538261077e565b9061063091602081830312611716576116f5565b61171e61061e565b6116f5565b506040513d6000823e3d90fd5b61173a6000610933565b81146118b65761181761175561175060066109c0565b610693565b61175e81610693565b926370a082319361176e30610693565b9161177860405190565b936117838760e01b90565b85526001600160a01b0384166004860152602085602481865afa9485156118a9575b60009561187f575b50916117c284926117fd9560209533906118ed565b6117e66117cf60066109c0565b976117d960405190565b9586948593849360e01b90565b83526001600160a01b031660048301526024820190565b03915afa908115611872575b600091611854575b50610d71565b9061184f6118457ff1fa0256e6ecba08316eb890d662830768e73136b5b0dbea4c9c8c16ca1747eb92610693565b9261023760405190565b0390a2565b61186c91506118633d82610dde565b3d810190611702565b38611811565b61187a611723565b611809565b60209391955084926117fd9561189c6117c2936118633d82610dde565b97939550509294506117ad565b6118b1611723565b6117a5565b5050604051611151565b610653906116a2565b6001600160a01b039182168152911660208201526060810192916106539160400152565b9061122790611218610653956004956119096323b872dd6111a1565b9361191360405190565b97889560208701908152016118c9565b91903361193361062460076109c0565b6001600160a01b03821614159081611961575b506119545761065392611987565b5050505061119260405190565b9050611971610a846106246109ca565b141538611946565b90610db6610630610ade9290565b9091600092611998610624856109f1565b6001600160a01b03841614611a54576119b084610933565b8114611a3757611a006002946119d0426119ca600a6106bb565b90610d88565b9586916119ed856119e56106308a600461069c565b928301611979565b6119fa8660018301611979565b01610da6565b61184f611a2d7f9ca93988e299d8264919a188c320f8d8efa97e1976a234f3c6c42315fc10b4e094610693565b946105ba60405190565b5050505050611a4560405190565b639dd854d360e01b8152600490fd5b5050505050611a6260405190565b63c1ab6dc160e01b8152600490fd5b906106539291611923565b61065390611a88610a6d565b600390611a98610fa2828461069c565b91600090611aa582610933565b8414611b025761103e83611abb611ac194610933565b9261069c565b611ad582611ace83610693565b33906111e4565b61184f6118457f9dc46f23cfb5ddcad0ae7ea2be38d47fec07bb9382ec7e564efc69e036dd66ce92610693565b505050505061115160405190565b61065390611a7c565b61065390611b25610a6d565b61065390611cc8565b61065390611b19565b61065390611b43610a6d565b61065390611c1c565b61065390611b37565b61065390611b61610a6d565b61065390611c77565b61065390611b55565b61065390611b7f610a6d565b611b8c61062460006109f1565b6001600160a01b03821614611be157611ba6816008610ac7565b611bd07fdfe91dcad2adcb1ecd18d2e830b469081211d6743c1e2dfa893836431fb7879491610693565b90611bda60405190565b808061184f565b505060405163e6c4247b60e01b8152600490fd5b61065390611b73565b61065390611c0a610a6d565b61065390611d2d565b61065390611bfe565b611c2961062460006109f1565b6001600160a01b03821614611c6d57611c43816006610ac7565b611bd07f2ffae89a4970d1ff9bf1a765b00d7eee23cd328cdaaab85b7ce9e5379c1ece3291610693565b5050604051611a62565b611c8461062460006109f1565b6001600160a01b03821614611be157611c9e816009610ac7565b611bd07f5326c5ebddd2feafb723c3b63cc83fb32a9c8dd7d05476832b798d9782bad87891610693565b611cd561062460006109f1565b6001600160a01b03821614611d1957611cef816007610ac7565b611bd07fdbebfba65bd6398fb722063efc10c99f624f9cd8ba657201056af918a676d5ee91610693565b505060405163ccea9e6f60e01b8152600490fd5b611d38610630610940565b8110611d7757611d4981600a610da6565b610cbf7f676dc704f22ddab12a141690a96616ef0128413803c63394aba7ffe658681a169161023760405190565b50506040516367e00a0760e01b8152600490fdfea364697066735822122048416a76561fc48128e8f000ff0712a4c8201c10df1cbc9e2d4fd939fbe4e8526c6578706572696d656e74616cf564736f6c634300080c0041

Verified Source Code Full Match

Compiler: v0.8.12+commit.f00d7308 EVM: london
RewardHarvester.sol 331 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Errors} from "./libraries/Errors.sol";
import {Common} from "./libraries/Common.sol";

contract RewardHarvester is Ownable2Step {
    using SafeERC20 for IERC20;

    struct Reward {
        bytes32 merkleRoot;
        bytes32 hashedData;
        uint256 activeAt;
    }

    uint256 public constant FEE_BASIS = 1_000_000;
    uint256 public constant MAX_FEE = 100_000;
    uint256 public constant MINIMUM_ACTIVE_TIMER = 3 hours;

    // Maps members
    mapping(address => bool) public isMember;
    // Maps fees collected for each token
    mapping(address => uint256) public feesCollected;
    // Maps each of the identifier to its reward metadata
    mapping(address => Reward) public rewards;
    // Tracks the amount of claimed reward for the specified token and account
    mapping(address => mapping(address => uint256)) public claimed;
    // Harvest default token
    address public defaultToken;
    // Operator address
    address public operator;
    // Claimer address
    address public claimer;
    // Reward swapper address
    address public rewardSwapper;
    // Used for calculating the timestamp on which rewards can be claimed after an update
    uint256 public activeTimerDuration;

    //-----------------------//
    //        Events         //
    //-----------------------//
    event MemberJoined(address member);
    event MemberLeft(address member);
    event FeesCollected(address indexed token, uint256 amount);
    event BribeTransferred(address indexed token, uint256 totalAmount);
    event RewardClaimed(
        address indexed token,
        address indexed account,
        uint256 amount,
        uint256 postFeeAmount,
        address receiver
    );
    event RewardMetadataUpdated(
        address indexed token,
        bytes32 merkleRoot,
        bytes32 proof,
        uint256 activeAt
    );
    event DefaultTokenUpdated(address indexed token);
    event SetOperator(address indexed operator);
    event SetClaimer(address indexed claimer);
    event SetRewardSwapper(address indexed rewardSwapper);
    event SetActiveTimerDuration(uint256 duration);

    //-----------------------//
    //       Modifiers       //
    //-----------------------//
    /**
     * @notice Modifier to check caller is operator
     */
    modifier onlyOperatorOrOwner() {
        if (msg.sender != operator && msg.sender != owner())
            revert Errors.NotAuthorized();
        _;
    }

    /**
     * @notice Modifier to check caller is operator or reward swapper
     */
    modifier onlyOperatorOrRewardSwapper() {
        if (msg.sender != operator && msg.sender != rewardSwapper)
            revert Errors.NotAuthorized();
        _;
    }

    //-----------------------//
    //       Constructor     //
    //-----------------------//
    constructor(
        address _rewardSwapper,
        address _operator,
        address _defaultToken
    ) {
        _setDefaultToken(_defaultToken);
        _setOperator(_operator);
        _setRewardSwapper(_rewardSwapper);
        _setActiveTimerDuration(MINIMUM_ACTIVE_TIMER);
    }

    //-----------------------//
    //   External Functions  //
    //-----------------------//

    /**
        @notice Join the harvester to enable claiming rewards in default token
     */
    function join() external {
        isMember[msg.sender] = true;

        emit MemberJoined(msg.sender);
    }

    /**
        @notice Leave harvester
     */
    function leave() external {
        isMember[msg.sender] = false;

        emit MemberLeft(msg.sender);
    }

    /**
        @notice Claim rewards based on the specified metadata
        @dev    Can only be called by the claimer contract
        @param  _token        address    Token to claim rewards
        @param  _account      address    Account to claim rewards
        @param  _amount       uint256    Amount of rewards to claim
        @param  _merkleProof  bytes32[]  Merkle proof of the claim
        @param  _fee          uint256    Claim fee
        @param  _receiver     address    Receiver of the rewards
     */
    function claim(
        address _token,
        address _account,
        uint256 _amount,
        bytes32[] calldata _merkleProof,
        uint256 _fee,
        address _receiver
    ) external {
        if (msg.sender != claimer) revert Errors.NotAuthorized();
        if (_account == address(0)) revert Errors.InvalidClaim();
        if (_amount == 0) revert Errors.InvalidAmount();
        if (_fee > MAX_FEE) revert Errors.InvalidFee();

        // Calculate amount after any fees
        uint256 feeAmount = (_amount * _fee) / FEE_BASIS;
        uint256 postFeeAmount = _amount - feeAmount;
        feesCollected[_token] += feeAmount;

        Reward memory reward = rewards[_token];
        uint256 lifeTimeAmount = claimed[_token][_account] + _amount;

        if (reward.merkleRoot == 0) revert Errors.InvalidDistribution();
        if (reward.activeAt > block.timestamp) revert Errors.RewardInactive();

        // Verify the merkle proof
        if (
            !MerkleProof.verifyCalldata(
                _merkleProof,
                reward.merkleRoot,
                keccak256(abi.encodePacked(_account, lifeTimeAmount))
            )
        ) revert Errors.InvalidProof();

        // Update the claimed amount to the current total
        claimed[_token][_account] = lifeTimeAmount;

        IERC20(_token).safeTransfer(_receiver, postFeeAmount);

        emit RewardClaimed(_token, _account, _amount, postFeeAmount, _receiver);
    }

    /**
        @notice Deposit `defaultToken` to this contract
        @param  _amount  uint256  Amount of `defaultToken` to deposit
     */
    function depositReward(
        uint256 _amount
    ) external onlyOperatorOrRewardSwapper {
        if (_amount == 0) revert Errors.InvalidAmount();

        IERC20 token = IERC20(defaultToken);

        uint256 initialAmount = token.balanceOf(address(this));

        token.safeTransferFrom(msg.sender, address(this), _amount);

        emit BribeTransferred(
            defaultToken,
            token.balanceOf(address(this)) - initialAmount
        );
    }

    /**
        @notice Update rewards metadata
        @param  _token       address  Token to update rewards metadata
        @param  _merkleRoot  bytes32  Merkle root of the rewards
        @param  _hashedData  bytes32  Hashed data of the rewards
     */
    function updateRewardsMetadata(
        address _token,
        bytes32 _merkleRoot,
        bytes32 _hashedData
    ) external onlyOperatorOrOwner {
        if (_token == address(0)) revert Errors.InvalidToken();
        if (_merkleRoot == 0) revert Errors.InvalidMerkleRoot();

        // Update the metadata and start the timer until the rewards will be active/claimable
        uint256 activeAt = block.timestamp + activeTimerDuration;
        Reward storage reward = rewards[_token];
        reward.merkleRoot = _merkleRoot;
        reward.hashedData = _hashedData;
        reward.activeAt = activeAt;

        emit RewardMetadataUpdated(_token, _merkleRoot, _hashedData, activeAt);
    }

    /**
        @notice Collect fees
        @param  _token  address  Token to collect fees
     */
    function collectFees(address _token) external onlyOwner {
        uint256 amount = feesCollected[_token];
        if (amount == 0) revert Errors.InvalidAmount();

        feesCollected[_token] = 0;
        IERC20(_token).safeTransfer(msg.sender, amount);

        emit FeesCollected(_token, amount);
    }

    /**
        @notice Change the operator
        @param  _operator  address  New operator address
     */
    function changeOperator(address _operator) external onlyOwner {
        _setOperator(_operator);
    }

    /**
        @notice Change the `defaultToken`
        @param  _newToken  address  New default token address
     */
    function changeDefaultToken(address _newToken) external onlyOwner {
        _setDefaultToken(_newToken);
    }

    /**
        @notice Change the RewardSwapper contract
        @param  _newSwapper  address  New reward swapper address
     */
    function changeRewardSwapper(address _newSwapper) external onlyOwner {
        _setRewardSwapper(_newSwapper);
    }

    /**
        @notice Change claimer address
        @param  _claimer  address  New claimer address
     */
    function changeClaimer(address _claimer) external onlyOwner {
        if (_claimer == address(0)) revert Errors.InvalidAddress();

        claimer = _claimer;

        emit SetClaimer(_claimer);
    }

    /**
        @notice Set the active timer duration
        @param  _duration  uint256  Timer duration
    */
    function changeActiveTimerDuration(uint256 _duration) external onlyOwner {
        _setActiveTimerDuration(_duration);
    }

    //-----------------------//
    //   Internal Functions  //
    //-----------------------//
    /**
        @dev    Internal to set the default token
        @param  _newToken  address  Token address
     */
    function _setDefaultToken(address _newToken) internal {
        if (_newToken == address(0)) revert Errors.InvalidToken();

        defaultToken = _newToken;

        emit DefaultTokenUpdated(_newToken);
    }

    /**
        @dev    Internal to set the RewardSwapper contract
        @param  _newSwapper  address  RewardSwapper address
     */
    function _setRewardSwapper(address _newSwapper) internal {
        if (_newSwapper == address(0)) revert Errors.InvalidAddress();

        rewardSwapper = _newSwapper;

        emit SetRewardSwapper(_newSwapper);
    }

    /**
        @dev    Internal to set the operator
        @param  _operator  address  Token address
     */
    function _setOperator(address _operator) internal {
        if (_operator == address(0)) revert Errors.InvalidOperator();

        operator = _operator;

        emit SetOperator(_operator);
    }

    /**
        @dev    Internal to set the active timer duration
        @param  _duration  uint256  Timer duration
     */
    function _setActiveTimerDuration(uint256 _duration) internal {
        if (_duration < MINIMUM_ACTIVE_TIMER)
            revert Errors.InvalidTimerDuration();

        activeTimerDuration = _duration;

        emit SetActiveTimerDuration(_duration);
    }
}
Common.sol 82 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

library Common {
    /**
     * @param identifier  bytes32  Identifier of the distribution
     * @param token       address  Address of the token to distribute
     * @param merkleRoot  bytes32  Merkle root of the distribution
     * @param proof       bytes32  Proof of the distribution
     */
    struct Distribution {
        bytes32 identifier;
        address token;
        bytes32 merkleRoot;
        bytes32 proof;
    }

    /**
     * @param proposal          bytes32  Proposal to bribe
     * @param token             address  Token to bribe with
     * @param briber            address  Address of the briber
     * @param amount            uint256  Amount of tokens to bribe with
     * @param maxTokensPerVote  uint256  Maximum amount of tokens to use per vote
     * @param periods           uint256  Number of periods to bribe for
     * @param periodDuration    uint256  Duration of each period
     * @param proposalDeadline  uint256  Deadline for the proposal
     * @param permitDeadline    uint256  Deadline for the permit2 signature
     * @param signature         bytes    Permit2 signature
     */
    struct DepositBribeParams {
        bytes32 proposal;
        address token;
        address briber;
        uint256 amount;
        uint256 maxTokensPerVote;
        uint256 periods;
        uint256 periodDuration;
        uint256 proposalDeadline;
        uint256 permitDeadline;
        bytes signature;
    }

    /**
     * @param rwIdentifier      bytes32    Identifier for claiming reward
     * @param fromToken         address    Address of token to swap from
     * @param toToken           address    Address of token to swap to
     * @param fromAmount        uint256    Amount of fromToken to swap
     * @param toAmount          uint256    Amount of toToken to receive
     * @param deadline          uint256    Timestamp until which swap may be fulfilled
     * @param callees           address[]  Array of addresses to call (DEX addresses)
     * @param callLengths       uint256[]  Index of the beginning of each call in exchangeData
     * @param values            uint256[]  Array of encoded values for each call in exchangeData
     * @param exchangeData      bytes      Calldata to execute on callees
     * @param rwMerkleProof     bytes32[]  Merkle proof for the reward claim
     */
    struct ClaimAndSwapData {
        bytes32 rwIdentifier;
        address fromToken;
        address toToken;
        uint256 fromAmount;
        uint256 toAmount;
        uint256 deadline;
        address[] callees;
        uint256[] callLengths;
        uint256[] values;
        bytes exchangeData;
        bytes32[] rwMerkleProof;
    }

    /**
     * @param identifier   bytes32    Identifier for claiming reward
     * @param account      address    Address of the account to claim for
     * @param amount       uint256    Amount of tokens to claim
     * @param merkleProof  bytes32[]  Merkle proof for the reward claim
     */
    struct Claim {
        bytes32 identifier;
        address account;
        uint256 amount;
        bytes32[] merkleProof;
    }
}
Errors.sol 224 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

library Errors {
    /**
     * @notice max period 0 or greater than MAX_PERIODS
     */
    error InvalidMaxPeriod();

    /**
     * @notice period duration 0 or greater than MAX_PERIOD_DURATION
     */
    error InvalidPeriodDuration();

    /**
     * @notice address provided is not a contract
     */
    error NotAContract();

    /**
     * @notice not authorized
     */
    error NotAuthorized();

    /**
     * @notice contract already initialized
     */
    error AlreadyInitialized();

    /**
     * @notice address(0)
     */
    error InvalidAddress();

    /**
     * @notice empty bytes identifier
     */
    error InvalidIdentifier();

    /**
     * @notice invalid protocol name
     */
    error InvalidProtocol();

    /**
     * @notice invalid number of choices
     */
    error InvalidChoiceCount();

    /**
     * @notice invalid input amount
     */
    error InvalidAmount();

    /**
     * @notice not team member
     */
    error NotTeamMember();

    /**
     * @notice cannot whitelist BRIBE_VAULT
     */
    error NoWhitelistBribeVault();

    /**
     * @notice token already whitelisted
     */
    error TokenWhitelisted();

    /**
     * @notice token not whitelisted
     */
    error TokenNotWhitelisted();

    /**
     * @notice voter already blacklisted
     */
    error VoterBlacklisted();

    /**
     * @notice voter not blacklisted
     */
    error VoterNotBlacklisted();

    /**
     * @notice deadline has passed
     */
    error DeadlinePassed();

    /**
     * @notice invalid period
     */
    error InvalidPeriod();

    /**
     * @notice invalid deadline
     */
    error InvalidDeadline();

    /**
     * @notice invalid max fee
     */
    error InvalidMaxFee();

    /**
     * @notice invalid fee
     */
    error InvalidFee();

    /**
     * @notice invalid fee recipient
     */
    error InvalidFeeRecipient();

    /**
     * @notice invalid distributor
     */
    error InvalidDistributor();

    /**
     * @notice invalid briber
     */
    error InvalidBriber();

    /**
     * @notice address does not have DEPOSITOR_ROLE
     */
    error NotDepositor();

    /**
     * @notice no array given
     */
    error InvalidArray();

    /**
     * @notice invalid reward identifier
     */
    error InvalidRewardIdentifier();

    /**
     * @notice bribe has already been transferred
     */
    error BribeAlreadyTransferred();

    /**
     * @notice distribution does not exist
     */
    error InvalidDistribution();

    /**
     * @notice invalid merkle root
     */
    error InvalidMerkleRoot();

    /**
     * @notice token is address(0)
     */
    error InvalidToken();

    /**
     * @notice claim does not exist
     */
    error InvalidClaim();

    /**
     * @notice reward is not yet active for claiming
     */
    error RewardInactive();

    /**
     * @notice timer duration is invalid
     */
    error InvalidTimerDuration();

    /**
     * @notice merkle proof is invalid
     */
    error InvalidProof();

    /**
     * @notice ETH transfer failed
     */
    error ETHTransferFailed();

    /**
     * @notice Invalid operator address
     */
    error InvalidOperator();

    /**
     * @notice call to TokenTransferProxy contract
     */
    error TokenTransferProxyCall();

    /**
     * @notice calling TransferFrom
     */
    error TransferFromCall();

    /**
     * @notice external call failed
     */
    error ExternalCallFailure();

    /**
     * @notice returned tokens too few
     */
    error InsufficientReturn();

    /**
     * @notice swapDeadline expired
     */
    error DeadlineBreach();

    /**
     * @notice expected tokens returned are 0
     */
    error ZeroExpectedReturns();

    /**
     * @notice arrays in SwapData.exchangeData have wrong lengths
     */
    error ExchangeDataArrayMismatch();
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
        }
    }
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @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;
    }
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}
Ownable2Step.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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.isContract(address(token));
    }
}
MerkleProof.sol 223 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @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 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}
     *
     * _Available since v4.7._
     */
    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.
     *
     * _Available since v4.4._
     */
    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}
     *
     * _Available since v4.7._
     */
    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.
     *
     * _Available since v4.7._
     */
    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.
     *
     * _Available since v4.7._
     */
    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).
     *
     * _Available since v4.7._
     */
    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 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // 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) {
            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.
     *
     * _Available since v4.7._
     */
    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 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // 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) {
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    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)
        }
    }
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @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.
 */
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].
     */
    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);
}

Read Contract

FEE_BASIS 0x8686ebcc → uint256
MAX_FEE 0xbc063e1a → uint256
MINIMUM_ACTIVE_TIMER 0x7fdba5f6 → uint256
activeTimerDuration 0x33f642f6 → uint256
claimed 0x0c9cbf0e → uint256
claimer 0xd379be23 → address
defaultToken 0x74c13fda → address
feesCollected 0x5b2478ea → uint256
isMember 0xa230c524 → bool
operator 0x570ca735 → address
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
rewardSwapper 0x5512b811 → address
rewards 0x0700037d → bytes32, bytes32, uint256

Write Contract 14 functions

These functions modify contract state and require a wallet transaction to execute.

acceptOwnership 0x79ba5097
No parameters
changeActiveTimerDuration 0x960d264d
uint256 _duration
changeClaimer 0x101cef48
address _claimer
changeDefaultToken 0x8e3fb0d3
address _newToken
changeOperator 0x06394c9b
address _operator
changeRewardSwapper 0x03aaf08b
address _newSwapper
claim 0x48a61ff5
address _token
address _account
uint256 _amount
bytes32[] _merkleProof
uint256 _fee
address _receiver
collectFees 0xa480ca79
address _token
depositReward 0x1e2720ff
uint256 _amount
join 0xb688a363
No parameters
leave 0xd66d9e19
No parameters
renounceOwnership 0x715018a6
No parameters
transferOwnership 0xf2fde38b
address newOwner
updateRewardsMetadata 0x0cc630c7
address _token
bytes32 _merkleRoot
bytes32 _hashedData

Recent Transactions

No transactions found for this address