Contract 0x74c0A3bD10634759DC8B4CA7078C8Bf85bFE1271

Contract Overview

Balance:
0 ETH

Token:
Txn Hash Method
Block
From
To
Value
0xa1a61d4d5bdb0f77d7edf3e0560df4c008889439e1091ddec56dd6fcdbd514e8New Merkle Root31958712022-12-05 4:49:18294 days 14 hrs ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH00.000000001
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c908Claim Multiple31476502022-12-02 18:22:20297 days 42 mins ago0x043e7c673f2bd9c62e69921395bfd1f97acc0a78 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH00.000000001
0x6f8ef9418af46d29daba18f2d9893779a19a945a2a6787bcba71b1bfa9d32efaClaim Multiple30719102022-11-29 17:26:44300 days 1 hr ago0xc2ecd777d06ffdf8b3179286beabf52b67e9d991 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0001123906630.000000001
0x17c02b74b9642504084ad9f11682dda0cf6eaabb7eea370771161a217643c712New Merkle Root30717042022-11-29 17:15:11300 days 1 hr ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0001081505130.000000001
0x59ca38072b485004f20e6f2b6e8f495a7e86c526a54140840d027faac1757af2Claim Multiple28790182022-11-22 22:11:46306 days 20 hrs ago0xc2ecd777d06ffdf8b3179286beabf52b67e9d991 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000004483490.000000001
0xa91a3d7762f2e397f99432118111199c25df716d637f8afe0e67733554570f43New Merkle Root28789912022-11-22 22:10:20306 days 20 hrs ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000004656260.000000001
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209aClaim Multiple27929902022-11-20 0:40:03309 days 18 hrs ago0xc2ecd777d06ffdf8b3179286beabf52b67e9d991 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000000000070.000000001
0x4498fb2dbb1556333561aedf63b7db401c7553112923e9ab34ed462f82f19ab3Claim Multiple26955062022-11-14 17:52:37315 days 1 hr ago0xf5cad67a16bb2db15ba76d665e62b2eb641de451 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.000288148330.000000001
0xe1948ab5540f0a89dac8e3b28dc22a005751f80291e6cbadd4cddc57a09e59b8New Merkle Root26769802022-11-14 0:00:02315 days 19 hrs ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH00.000000001
0x614dc2b9209daf0e6f0e1efedee81600ecb26e392b907919891a1427faefe9ceNew Merkle Root25121952022-11-08 3:03:10321 days 16 hrs ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0001889745130.000000001
0x47bf19cf1b0580b635bea0f0782110eb9fdf8ffad8da1f4dfd94c49c641ccfc7Accept Ownership25110252022-11-08 1:31:12321 days 17 hrs ago0x246100ec9dfcf22194316a187b38905906539b41 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000638289810.000000001
0x240f1387f5faf14d03b9be88d28fd9b505acbf1bcaff48aa53be30f8041acf88Nominate New Own...25110082022-11-08 1:29:39321 days 17 hrs ago0x652c46a302060b324a02d2d3e4a56e3da07fa91b IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000596433540.000000001
0xbf57f7a78e78130b33b4d5a6603d9f50f09730e29af594ee579b4f98ae2a40b2Claim25046942022-11-07 16:41:00322 days 2 hrs ago0x043e7c673f2bd9c62e69921395bfd1f97acc0a78 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0003366358910.000000001
0x36e19d429021b92f890a6247771adc1e2aa97b1085bb857a1f8c3542ed80c8a6Claim25046822022-11-07 16:40:23322 days 2 hrs ago0x043e7c673f2bd9c62e69921395bfd1f97acc0a78 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0002909965710.000000001
0xf2b8266dd0db7b337bd3d1b964a0e7adaf2a8d71d93b43cefc519d7a75dd57d4Claim24574732022-11-04 18:22:08325 days 42 mins ago0xf5cad67a16bb2db15ba76d665e62b2eb641de451 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0000804560520.000000001
0x9546debeca7c4ac5399c20c53ae029b17fa0e09c7c6b5ed24d5f9521203740fdNew Merkle Root24554582022-11-04 15:46:03325 days 3 hrs ago0x652c46a302060b324a02d2d3e4a56e3da07fa91b IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0004661676090.000000001
0xda3a240f7cad525bdad10e145a39389a955acb57b7f09252f538a08bcc7a5bd4Claim24398062022-11-03 18:34:22326 days 30 mins ago0xc2ecd777d06ffdf8b3179286beabf52b67e9d991 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0001529667290.000000001
0x75cc42bd46787bed93511c6048d1cee9f3bd7c55692914f5d5336ef65d5c3d61Claim24353942022-11-03 13:18:00326 days 5 hrs ago0xc2ecd777d06ffdf8b3179286beabf52b67e9d991 IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0002846358420.000000001
0xe6b1d521302d0cf22b8e273a333d9720daa57598c0fac7e251ac99988f48589aNew Merkle Root23096202022-10-27 3:18:12333 days 15 hrs ago0x652c46a302060b324a02d2d3e4a56e3da07fa91b IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH0.0004308980080.000000001
0x2d4b8fbc5ded6e16e7cc411b83022950ace967168f7f2dbf839bfccc36ba9dcfNominate New Own...15901002022-09-30 23:56:06359 days 19 hrs ago0x652c46a302060b324a02d2d3e4a56e3da07fa91b IN  0x74c0a3bd10634759dc8b4ca7078c8bf85bfe12710 ETH00.000000001
0x7ce114f9b5e4192c2f74974e69694fcd7382bc1423819f0c6061f667b2229a450x60c0604015900802022-09-30 23:55:21359 days 19 hrs ago0x652c46a302060b324a02d2d3e4a56e3da07fa91b IN  Create: MultipleMerkleDistributor0 ETH0.0000000000020.000000001
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x90f11bcd733dbb742aa861fab984e0deb911a3152677bca28904ae7d8635c90831476502022-12-02 18:22:20297 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x6f8ef9418af46d29daba18f2d9893779a19a945a2a6787bcba71b1bfa9d32efa30719102022-11-29 17:26:44300 days 1 hr ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x6f8ef9418af46d29daba18f2d9893779a19a945a2a6787bcba71b1bfa9d32efa30719102022-11-29 17:26:44300 days 1 hr ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x59ca38072b485004f20e6f2b6e8f495a7e86c526a54140840d027faac1757af228790182022-11-22 22:11:46306 days 20 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x59ca38072b485004f20e6f2b6e8f495a7e86c526a54140840d027faac1757af228790182022-11-22 22:11:46306 days 20 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0xfc9dbb9f4c788e10f8134136f1a4f7a1bee05c5217dc4c8ba78c395a06fe209a27929902022-11-20 0:40:03309 days 18 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x4498fb2dbb1556333561aedf63b7db401c7553112923e9ab34ed462f82f19ab326955062022-11-14 17:52:37315 days 1 hr ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x4498fb2dbb1556333561aedf63b7db401c7553112923e9ab34ed462f82f19ab326955062022-11-14 17:52:37315 days 1 hr ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0x36e19d429021b92f890a6247771adc1e2aa97b1085bb857a1f8c3542ed80c8a625046822022-11-07 16:40:23322 days 2 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0x36e19d429021b92f890a6247771adc1e2aa97b1085bb857a1f8c3542ed80c8a625046822022-11-07 16:40:23322 days 2 hrs ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0xf2b8266dd0db7b337bd3d1b964a0e7adaf2a8d71d93b43cefc519d7a75dd57d424574732022-11-04 18:22:08325 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
0xf2b8266dd0db7b337bd3d1b964a0e7adaf2a8d71d93b43cefc519d7a75dd57d424574732022-11-04 18:22:08325 days 42 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xda0c33402fc1e10d18c532f0ed9c1a6c5c9e386c0 ETH
0xda3a240f7cad525bdad10e145a39389a955acb57b7f09252f538a08bcc7a5bd424398062022-11-03 18:34:22326 days 30 mins ago 0x74c0a3bd10634759dc8b4ca7078c8bf85bfe1271 0xafd87d1a62260bd5714c55a1bb4057bdc8dfa4130 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MultipleMerkleDistributor

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 55 : ControlL2MerkleDistributor.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "./interfaces/IControlL2MerkleDistributor.sol";
import {ICrossDomainMessenger} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";

/// @title Kwenta ControlL2MerkleDistributor
/// @author JaredBorders
/// @notice This L1 deployed contract is responsible for communicating with the
/// MerkleDistributor deployed on L2 (Optimism Mainnet)
contract ControlL2MerkleDistributor is IControlL2MerkleDistributor {
    /// @notice communication between L1 and L2 is enabled by two special
    /// smart contracts called the "messengers"
    /// and below is the address for the messenger on L1
    address internal immutable crossDomainMessengerAddr;

    /// @notice MerkleDistributor deployed on L2
    address internal immutable merkleDistributorL2Address;

    /// @notice set addresses for deployed MerkleDistributor on L2 and
    /// OE cross domain messenger address on L1
    /// @param _crossDomainMessengerAddr: messenger on L1 enabling communication to L2
    /// @param _merkleDistributorL2Address: Kwenta MerkleDistributor on L2
    constructor(
        address _crossDomainMessengerAddr,
        address _merkleDistributorL2Address
    ) {
        crossDomainMessengerAddr = _crossDomainMessengerAddr;
        merkleDistributorL2Address = _merkleDistributorL2Address;
    }

    /// @notice claim $KWENTA on L2 from an L1 address
    /// @dev destAccount will be the address used to create new escrow entry
    /// @dev the function caller (i.e. msg.sender) will be provided as a parameter
    /// to MerkleDistributor.claimToAddress() on L2. Only valid callers will
    /// be able to claim
    /// @param index: used for merkle tree managment and verification
    /// @param destAccount: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claimToAddress(
        uint256 index,
        address destAccount,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external override {
        bytes memory message;
        message = abi.encodeWithSignature(
            "claimToAddress(uint256,address,address,uint256,bytes32[])",
            index,
            /// @notice account to be verified in merkle tree
            msg.sender,
            /// @notice address used for escrow entry
            /// @dev does not necessarily have to be different from msg.sender
            destAccount,
            amount,
            merkleProof
        );

        /// @notice send message to CrossDomainMessenger which will communicate message to L2
        ICrossDomainMessenger(crossDomainMessengerAddr).sendMessage(
            merkleDistributorL2Address,
            message,
            1000000 // within the free gas limit amount
        );
    }
}

File 2 of 55 : IControlL2MerkleDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// allows messages from L1 -> L2
interface IControlL2MerkleDistributor {
    /// @notice claim $KWENTA on L2 from an L1 address
    /// @dev destAccount will be the address used to create new escrow entry
    /// @dev the function caller (i.e. msg.sender) will be provided as a parameter
    /// to MerkleDistributor.claimToAddress() on L2. Only valid callers will
    /// be able to claim
    /// @param index: used for merkle tree managment and verification
    /// @param destAccount: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claimToAddress(uint256 index, address destAccount, uint256 amount, bytes32[] calldata merkleProof) external;
}

File 3 of 55 : ICrossDomainMessenger.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

/**
 * @title ICrossDomainMessenger
 */
interface ICrossDomainMessenger {
    /**********
     * Events *
     **********/

    event SentMessage(
        address indexed target,
        address sender,
        bytes message,
        uint256 messageNonce,
        uint256 gasLimit
    );
    event RelayedMessage(bytes32 indexed msgHash);
    event FailedRelayedMessage(bytes32 indexed msgHash);

    /*************
     * Variables *
     *************/

    function xDomainMessageSender() external view returns (address);

    /********************
     * Public Functions *
     ********************/

    /**
     * Sends a cross domain message to the target messenger.
     * @param _target Target contract address.
     * @param _message Message to send to the target.
     * @param _gasLimit Gas limit for the provided message.
     */
    function sendMessage(
        address _target,
        bytes calldata _message,
        uint32 _gasLimit
    ) external;
}

File 4 of 55 : MerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./utils/Owned.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./interfaces/IRewardEscrow.sol";
import "./interfaces/IMerkleDistributor.sol";
import {ICrossDomainMessenger} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";

/// @title Kwenta MerkleDistributor
/// @author JaredBorders
/// @notice Facilitates $KWENTA distribution via Merkle Proof verification
contract MerkleDistributor is IMerkleDistributor, Owned {
    /// @notice escrow for tokens claimed
    address public immutable override rewardEscrow;

    /// @notice token to be distributed (KWENTA)
    address public immutable override token;

    /// @notice contract that initiates claim from L1 (called by address attempting to claim)
    /// @dev can only be set by owner
    address public override controlL2MerkleDistributor;

    /// @notice the merkle root of the merkle tree containing account balances available to claim
    bytes32 public immutable override merkleRoot;

    /// @notice communication between L1 and L2 is enabled by two special
    /// smart contracts called the "messengers" and below is the
    /// address for the messenger on L2
    address private constant crossDomainMessengerAddr =
        0x4200000000000000000000000000000000000007;

    /// @notice this is a packed array of booleans
    mapping(uint256 => uint256) private claimedBitMap;

    /// @notice set addresses for deployed rewardEscrow and KWENTA.
    /// Establish merkle root for verification
    /// @param _owner: designated owner of this contract
    /// @param _token: address of erc20 token to be distributed
    /// @param _rewardEscrow: address of kwenta escrow for tokens claimed
    /// @param _merkleRoot: used for claim verification
    constructor(
        address _owner,
        address _token,
        address _rewardEscrow,
        bytes32 _merkleRoot
    ) Owned(_owner) {
        token = _token;
        rewardEscrow = _rewardEscrow;
        merkleRoot = _merkleRoot;
    }

    /// @notice owner can set address of ControlL2MerkleDistributor
    /// @dev this function must be called after (1) this contract has been deployed and
    /// (2) ControlL2MerkleDistributor has been deployed (which requires this contract's
    /// deployment address as input in the constructor)
    /// @param _controlL2MerkleDistributor: address of contract that initiates claim from L1
    function setControlL2MerkleDistributor(address _controlL2MerkleDistributor)
        external
        override
        onlyOwner
    {
        controlL2MerkleDistributor = _controlL2MerkleDistributor;
    }

    /// @notice determine if indexed claim has been claimed
    /// @param index: used for claim managment
    /// @return true if indexed claim has been claimed
    function isClaimed(uint256 index) public view override returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMap[claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }

    /// @notice set claimed status for indexed claim to true
    /// @param index: used for claim managment
    function _setClaimed(uint256 index) private {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        claimedBitMap[claimedWordIndex] =
            claimedBitMap[claimedWordIndex] |
            (1 << claimedBitIndex);
    }

    /// @notice attempt to claim as `account` and escrow KWENTA for `account`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external override {
        require(!isClaimed(index), "MerkleDistributor: Drop already claimed.");

        // verify the merkle proof
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(
            MerkleProof.verify(merkleProof, merkleRoot, node),
            "MerkleDistributor: Invalid proof."
        );

        // mark it claimed and send the token to RewardEscrow
        _setClaimed(index);
        IERC20(token).approve(rewardEscrow, amount);
        IRewardEscrow(rewardEscrow).createEscrowEntry(
            account,
            amount,
            52 weeks
        );

        emit Claimed(index, account, amount);
    }

    /// @notice attempt to claim as `account` and escrow KWENTA for `destAccount`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address that initiated claim and designated `destAccount`
    /// @param destAccount: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claimToAddress(
        uint256 index,
        address account,
        address destAccount,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external override {
        require(!isClaimed(index), "MerkleDistributor: Drop already claimed.");

        /// @notice function caller must be L2 Cross Domain Messenger
        require(
            msg.sender == crossDomainMessengerAddr,
            "MerkleDistributor: Only the OVM-ICrossDomainMessenger can call this function"
        );

        /// @notice if controlL2MerkleDistributor has NOT been set, function will revert
        require(
            controlL2MerkleDistributor != address(0),
            "MerkleDistributor: controlL2MerkleDistributor has not been set by owner"
        );

        /// @notice L1 contract which called L1 Cross Domain Messenger
        /// must be controlL2MerkleDistributor
        require(
            controlL2MerkleDistributor ==
                ICrossDomainMessenger(crossDomainMessengerAddr)
                    .xDomainMessageSender(),
            "MerkleDistributor: xDomainMessageSender must be controlL2MerkleDistributor"
        );

        // verify the merkle proof with the L1 `account` address
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(
            MerkleProof.verify(merkleProof, merkleRoot, node),
            "MerkleDistributor: Invalid proof."
        );

        // mark it claimed and send the token to RewardEscrow
        _setClaimed(index);
        IERC20(token).approve(rewardEscrow, amount);

        // @notice `destAccount` is used for escrow, NOT `account`
        IRewardEscrow(rewardEscrow).createEscrowEntry(
            destAccount,
            amount,
            52 weeks
        );

        emit Claimed(index, account, amount);
    }
}

File 5 of 55 : Owned.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// https://docs.synthetix.io/contracts/source/contracts/owned
contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor(address _owner) {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}

File 6 of 55 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

File 7 of 55 : MerkleProof.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 */
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) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}

File 8 of 55 : IRewardEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library VestingEntries {
    struct VestingEntry {
        uint64 endTime;
        uint256 escrowAmount;
        uint256 duration;
    }
    struct VestingEntryWithID {
        uint64 endTime;
        uint256 escrowAmount;
        uint256 entryID;
    }
}

interface IRewardEscrow {
    // Views
    function getKwentaAddress() external view returns (address);

    function balanceOf(address account) external view returns (uint256);

    function numVestingEntries(address account) external view returns (uint256);

    function totalEscrowedAccountBalance(address account)
        external
        view
        returns (uint256);

    function totalVestedAccountBalance(address account)
        external
        view
        returns (uint256);

    function getVestingQuantity(address account, uint256[] calldata entryIDs)
        external
        view
        returns (uint256, uint256);

    function getVestingSchedules(
        address account,
        uint256 index,
        uint256 pageSize
    ) external view returns (VestingEntries.VestingEntryWithID[] memory);

    function getAccountVestingEntryIDs(
        address account,
        uint256 index,
        uint256 pageSize
    ) external view returns (uint256[] memory);

    function getVestingEntryClaimable(address account, uint256 entryID)
        external
        view
        returns (uint256, uint256);

    function getVestingEntry(address account, uint256 entryID)
        external
        view
        returns (
            uint64,
            uint256,
            uint256
        );

    // Mutative functions
    function vest(uint256[] calldata entryIDs) external;

    function createEscrowEntry(
        address beneficiary,
        uint256 deposit,
        uint256 duration
    ) external;

    function appendVestingEntry(
        address account,
        uint256 quantity,
        uint256 duration
    ) external;

    function stakeEscrow(uint256 _amount) external;

    function unstakeEscrow(uint256 _amount) external;
}

File 9 of 55 : IMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0;

// Allows anyone to claim a token if they exist in a merkle root.
interface IMerkleDistributor {
    /// @notice event is triggered whenever a call to `claim` succeeds
    event Claimed(uint256 index, address account, uint256 amount);

    /// @return escrow for tokens claimed
    function rewardEscrow() external view returns (address);

    /// @return token to be distributed (KWENTA)
    function token() external view returns (address);

    /// @return contract that initiates claim from L1 (called by address attempting to claim)
    function controlL2MerkleDistributor() external view returns (address);

    // @return the merkle root of the merkle tree containing account balances available to claim
    function merkleRoot() external view returns (bytes32);

    /// @notice owner can set address of ControlL2MerkleDistributor
    /// @dev this function must be called after (1) this contract has been deployed and
    /// (2) ControlL2MerkleDistributor has been deployed (which requires this contract's
    /// deployment address as input in the constructor)
    /// @param _controlL2MerkleDistributor: address of contract that initiates claim from L1
    function setControlL2MerkleDistributor(address _controlL2MerkleDistributor)
        external;

    /// @notice determine if indexed claim has been claimed
    /// @param index: used for claim managment
    /// @return true if indexed claim has been claimed
    function isClaimed(uint256 index) external view returns (bool);

    /// @notice attempt to claim as `account` and escrow KWENTA for `account`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external;

    /// @notice attempt to claim as `account` and escrow KWENTA for `destAccount`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address that initiated claim and designated `destAccount`
    /// @param destAccount: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    function claimToAddress(
        uint256 index,
        address account,
        address destAccount,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external;
}

File 10 of 55 : StakingRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./utils/Owned.sol";
import "./interfaces/IStakingRewards.sol";
import "./interfaces/ISupplySchedule.sol";
import "./interfaces/IRewardEscrow.sol";

/// @title KWENTA Staking Rewards
/// @author SYNTHETIX, JaredBorders ([email protected]), JChiaramonte7 ([email protected])
/// @notice Updated version of Synthetix's StakingRewards with new features specific
/// to Kwenta
contract StakingRewards is IStakingRewards, Owned, ReentrancyGuard, Pausable {
    using SafeERC20 for IERC20;

    /*///////////////////////////////////////////////////////////////
                                CONSTANTS
    ///////////////////////////////////////////////////////////////*/

    /// @notice token used for BOTH staking and rewards
    IERC20 public immutable token;

    /// @notice escrow contract which holds (and may stake) reward tokens
    IRewardEscrow public immutable rewardEscrow;

    /// @notice handles reward token minting logic
    ISupplySchedule public immutable supplySchedule;

    /*///////////////////////////////////////////////////////////////
                                STATE
    ///////////////////////////////////////////////////////////////*/

    /// @notice number of tokens staked by address
    /// @dev this includes escrowed tokens stake
    mapping(address => uint256) private balances;

    /// @notice number of staked escrow tokens by address
    mapping(address => uint256) private escrowedBalances;

    /// @notice marks applicable reward period finish time
    uint256 public periodFinish = 0;

    /// @notice amount of tokens minted per second
    uint256 public rewardRate = 0;

    /// @notice period for rewards
    uint256 public rewardsDuration = 7 days;

    /// @notice track last time the rewards were updated
    uint256 public lastUpdateTime;

    /// @notice summation of rewardRate divided by total staked tokens
    uint256 public rewardPerTokenStored;

    /// @notice total number of tokens staked in this contract
    uint256 public _totalSupply;

    /// @notice represents the rewardPerToken
    /// value the last time the stake calculated earned() rewards
    mapping(address => uint256) public userRewardPerTokenPaid;

    /// @notice track rewards for a given user which changes when
    /// a user stakes, unstakes, or claims rewards
    mapping(address => uint256) public rewards;

    /*///////////////////////////////////////////////////////////////
                                EVENTS
    ///////////////////////////////////////////////////////////////*/

    /// @notice update reward rate
    /// @param reward: amount to be distributed over applicable rewards duration
    event RewardAdded(uint256 reward);

    /// @notice emitted when user stakes tokens
    /// @param user: staker address
    /// @param amount: amount staked
    event Staked(address indexed user, uint256 amount);

    /// @notice emitted when user unstakes tokens
    /// @param user: address of user unstaking
    /// @param amount: amount unstaked
    event Unstaked(address indexed user, uint256 amount);

    /// @notice emitted when escrow staked
    /// @param user: owner of escrowed tokens address
    /// @param amount: amount staked
    event EscrowStaked(address indexed user, uint256 amount);

    /// @notice emitted when staked escrow tokens are unstaked
    /// @param user: owner of escrowed tokens address
    /// @param amount: amount unstaked
    event EscrowUnstaked(address user, uint256 amount);

    /// @notice emitted when user claims rewards
    /// @param user: address of user claiming rewards
    /// @param reward: amount of reward token claimed
    event RewardPaid(address indexed user, uint256 reward);

    /// @notice emitted when rewards duration changes
    /// @param newDuration: denoted in seconds
    event RewardsDurationUpdated(uint256 newDuration);

    /// @notice emitted when tokens are recovered from this contract
    /// @param token: address of token recovered
    /// @param amount: amount of token recovered
    event Recovered(address token, uint256 amount);

    /*///////////////////////////////////////////////////////////////
                                AUTH
    ///////////////////////////////////////////////////////////////*/

    /// @notice access control modifier for rewardEscrow
    modifier onlyRewardEscrow() {
        require(
            msg.sender == address(rewardEscrow),
            "StakingRewards: Only Reward Escrow"
        );
        _;
    }

    /// @notice access control modifier for rewardEscrow
    modifier onlySupplySchedule() {
        require(
            msg.sender == address(supplySchedule),
            "StakingRewards: Only Supply Schedule"
        );
        _;
    }

    /*///////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    ///////////////////////////////////////////////////////////////*/

    /// @notice configure StakingRewards state
    /// @dev owner set to address that deployed StakingRewards
    /// @param _token: token used for staking and for rewards
    /// @param _rewardEscrow: escrow contract which holds (and may stake) reward tokens
    /// @param _supplySchedule: handles reward token minting logic
    constructor(
        address _token,
        address _rewardEscrow,
        address _supplySchedule
    ) Owned(msg.sender) {
        // define reward/staking token
        token = IERC20(_token);

        // define contracts which will interact with StakingRewards
        rewardEscrow = IRewardEscrow(_rewardEscrow);
        supplySchedule = ISupplySchedule(_supplySchedule);
    }

    /*///////////////////////////////////////////////////////////////
                                VIEWS
    ///////////////////////////////////////////////////////////////*/

    /// @dev returns staked tokens which will likely not be equal to total tokens
    /// in the contract since reward and staking tokens are the same
    /// @return total amount of tokens that are being staked
    function totalSupply() external view override returns (uint256) {
        return _totalSupply;
    }

    /// @param account: address of potential staker
    /// @return amount of tokens staked by account
    function balanceOf(address account)
        external
        view
        override
        returns (uint256)
    {
        return balances[account];
    }

    /// @notice Getter function for number of staked escrow tokens
    /// @param account address to check the escrowed tokens staked
    /// @return amount of escrowed tokens staked
    function escrowedBalanceOf(address account)
        external
        view
        override
        returns (uint256)
    {
        return escrowedBalances[account];
    }

    /// @return rewards for the duration specified by rewardsDuration
    function getRewardForDuration() external view override returns (uint256) {
        return rewardRate * rewardsDuration;
    }

    /// @notice Getter function for number of staked non-escrow tokens
    /// @param account address to check the non-escrowed tokens staked
    /// @return amount of non-escrowed tokens staked
    function nonEscrowedBalanceOf(address account)
        public
        view
        override
        returns (uint256)
    {
        return balances[account] - escrowedBalances[account];
    }

    /*///////////////////////////////////////////////////////////////
                            STAKE/UNSTAKE
    ///////////////////////////////////////////////////////////////*/

    /// @notice stake token
    /// @param amount: amount to stake
    /// @dev updateReward() called prior to function logic
    function stake(uint256 amount)
        external
        override
        nonReentrant
        whenNotPaused
        updateReward(msg.sender)
    {
        require(amount > 0, "StakingRewards: Cannot stake 0");

        // update state
        _totalSupply += amount;
        balances[msg.sender] += amount;

        // transfer token to this contract from the caller
        token.safeTransferFrom(msg.sender, address(this), amount);

        // emit staking event and index msg.sender
        emit Staked(msg.sender, amount);
    }

    /// @notice unstake token
    /// @param amount: amount to unstake
    /// @dev updateReward() called prior to function logic
    function unstake(uint256 amount)
        public
        override
        nonReentrant
        updateReward(msg.sender)
    {
        require(amount > 0, "StakingRewards: Cannot Unstake 0");
        require(
            amount <= nonEscrowedBalanceOf(msg.sender),
            "StakingRewards: Invalid Amount"
        );

        // update state
        _totalSupply -= amount;
        balances[msg.sender] -= amount;

        // transfer token from this contract to the caller
        token.safeTransfer(msg.sender, amount);

        // emit unstake event and index msg.sender
        emit Unstaked(msg.sender, amount);
    }

    /// @notice stake escrowed token
    /// @param account: address which owns token
    /// @param amount: amount to stake
    /// @dev updateReward() called prior to function logic
    /// @dev msg.sender NOT used (account is used)
    function stakeEscrow(address account, uint256 amount)
        external
        override
        whenNotPaused
        onlyRewardEscrow
        updateReward(account)
    {
        require(amount > 0, "StakingRewards: Cannot stake 0");

        // update state
        balances[account] += amount;
        escrowedBalances[account] += amount;

        // updates total supply despite no new staking token being transfered.
        // escrowed tokens are locked in RewardEscrow
        _totalSupply += amount;

        // emit escrow staking event and index _account
        emit EscrowStaked(account, amount);
    }

    /// @notice unstake escrowed token
    /// @param account: address which owns token
    /// @param amount: amount to unstake
    /// @dev updateReward() called prior to function logic
    /// @dev msg.sender NOT used (account is used)
    function unstakeEscrow(address account, uint256 amount)
        external
        override
        nonReentrant
        onlyRewardEscrow
        updateReward(account)
    {
        require(amount > 0, "StakingRewards: Cannot Unstake 0");
        require(
            escrowedBalances[account] >= amount,
            "StakingRewards: Invalid Amount"
        );

        // update state
        balances[account] -= amount;
        escrowedBalances[account] -= amount;

        // updates total supply despite no new staking token being transfered.
        // escrowed tokens are locked in RewardEscrow
        _totalSupply -= amount;

        // emit escrow unstaked event and index account
        emit EscrowUnstaked(account, amount);
    }

    /// @notice unstake all available staked non-escrowed tokens and
    /// claim any rewards
    function exit() external override {
        unstake(nonEscrowedBalanceOf(msg.sender));
        getReward();
    }    

    /*///////////////////////////////////////////////////////////////
                            CLAIM REWARDS
    ///////////////////////////////////////////////////////////////*/

    /// @notice caller claims any rewards generated from staking
    /// @dev rewards are escrowed in RewardEscrow
    /// @dev updateReward() called prior to function logic
    function getReward() public override nonReentrant updateReward(msg.sender) {
        uint256 reward = rewards[msg.sender];
        if (reward > 0) {
            // update state (first)
            rewards[msg.sender] = 0;

            // transfer token from this contract to the caller
            token.safeTransfer(address(rewardEscrow), reward);
            rewardEscrow.appendVestingEntry(msg.sender, reward, 52 weeks);

            // emit reward claimed event and index msg.sender
            emit RewardPaid(msg.sender, reward);
        }
    }

    /*///////////////////////////////////////////////////////////////
                        REWARD UPDATE CALCULATIONS
    ///////////////////////////////////////////////////////////////*/

    /// @notice update reward state for the account and contract
    /// @param account: address of account which rewards are being updated for
    /// @dev contract state not specific to an account will be updated also
    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();

        if (account != address(0)) {
            // update amount of rewards a user can claim
            rewards[account] = earned(account);

            // update reward per token staked AT this given time
            // (i.e. when this user is interacting with StakingRewards)
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    /// @notice calculate running sum of reward per total tokens staked
    /// at this specific time
    /// @return running sum of reward per total tokens staked
    function rewardPerToken() public view override returns (uint256) {
        if (_totalSupply == 0) {
            return rewardPerTokenStored;
        }

        return
            rewardPerTokenStored +
            (((lastTimeRewardApplicable() - lastUpdateTime) *
                rewardRate *
                1e18) / (_totalSupply));
    }

    /// @return timestamp of the last time rewards are applicable
    function lastTimeRewardApplicable() public view override returns (uint256) {
        return block.timestamp < periodFinish ? block.timestamp : periodFinish;
    }

    /// @notice determine how much reward token an account has earned thus far
    /// @param account: address of account earned amount is being calculated for
    function earned(address account) public view override returns (uint256) {
        return
            ((balances[account] *
                (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18) +
            rewards[account];
    }

    /*///////////////////////////////////////////////////////////////
                            SETTINGS
    ///////////////////////////////////////////////////////////////*/

    /// @notice configure reward rate
    /// @param reward: amount of token to be distributed over a period
    /// @dev updateReward() called prior to function logic (with zero address)
    function notifyRewardAmount(uint256 reward)
        external
        override
        onlySupplySchedule
        updateReward(address(0))
    {
        if (block.timestamp >= periodFinish) {
            rewardRate = reward / rewardsDuration;
        } else {
            uint256 remaining = periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardRate;
            rewardRate = (reward + leftover) / rewardsDuration;
        }

        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp + rewardsDuration;
        emit RewardAdded(reward);
    }

    /// @notice set rewards duration
    /// @param _rewardsDuration: denoted in seconds
    function setRewardsDuration(uint256 _rewardsDuration)
        external
        override
        onlyOwner
    {
        require(
            block.timestamp > periodFinish,
            "StakingRewards: Previous rewards period must be complete before changing the duration for the new period"
        );
        rewardsDuration = _rewardsDuration;
        emit RewardsDurationUpdated(rewardsDuration);
    }

    /*///////////////////////////////////////////////////////////////
                            PAUSABLE
    ///////////////////////////////////////////////////////////////*/

    /// @dev Triggers stopped state
    function pauseStakingRewards() external override onlyOwner {
        Pausable._pause();
    }

    /// @dev Returns to normal state.
    function unpauseStakingRewards() external override onlyOwner {
        Pausable._unpause();
    }

    /*///////////////////////////////////////////////////////////////
                            MISCELLANEOUS
    ///////////////////////////////////////////////////////////////*/

    /// @notice added to support recovering LP Rewards from other systems
    /// such as BAL to be distributed to holders
    /// @param tokenAddress: address of token to be recovered
    /// @param tokenAmount: amount of token to be recovered
    function recoverERC20(address tokenAddress, uint256 tokenAmount)
        external
        override
        onlyOwner
    {
        require(
            tokenAddress != address(token),
            "StakingRewards: Cannot unstake the staking token"
        );
        IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
        emit Recovered(tokenAddress, tokenAmount);
    }
}

File 11 of 55 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.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;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    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));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    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");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @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");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 12 of 55 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 13 of 55 : Pausable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 14 of 55 : IStakingRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IStakingRewards {
    /// VIEWS
    // token state
    function totalSupply() external view returns (uint256);
    // staking state
    function balanceOf(address account) external view returns (uint256);
    function escrowedBalanceOf(address account) external view returns (uint256);
    function nonEscrowedBalanceOf(address account) external view returns (uint256);
    // rewards
    function getRewardForDuration() external view returns (uint256);
    function rewardPerToken() external view returns (uint256);
    function lastTimeRewardApplicable() external view returns (uint256);
    function earned(address account) external view returns (uint256);

    /// MUTATIVE
    // Staking/Unstaking
    function stake(uint256 amount) external;
    function unstake(uint256 amount) external;
    function stakeEscrow(address account, uint256 amount) external;
    function unstakeEscrow(address account, uint256 amount) external;
    function exit() external;
    // claim rewards
    function getReward() external;
    // settings
    function notifyRewardAmount(uint256 reward) external;
    function setRewardsDuration(uint256 _rewardsDuration) external;
    // pausable
    function pauseStakingRewards() external;
    function unpauseStakingRewards() external;
    // misc.
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external;
}

File 15 of 55 : ISupplySchedule.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.24;

interface ISupplySchedule {
    // Views
    function mintableSupply() external view returns (uint);

    function isMintable() external view returns (bool);

    // Mutative functions

    function mint() external;

    function setTreasuryDiversion(uint _treasuryDiversion) external;

    function setTradingRewardsDiversion(uint _tradingRewardsDiversion) external;
    
    function setStakingRewards(address _stakingRewards) external;

    function setTradingRewards(address _tradingRewards) external;
}

File 16 of 55 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 17 of 55 : Context.sol
// SPDX-License-Identifier: MIT

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;
    }
}

File 18 of 55 : SupplySchedule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Inheritance
import "./utils/Owned.sol";
import "./interfaces/ISupplySchedule.sol";

// Libraries
import "./libraries/SafeDecimalMath.sol";
import "./libraries/Math.sol";

// Internal references
import "./interfaces/IERC20.sol";
import "./interfaces/IKwenta.sol";
import "./interfaces/IStakingRewards.sol";
import "./interfaces/IMultipleMerkleDistributor.sol";

// https://docs.synthetix.io/contracts/source/contracts/supplyschedule
contract SupplySchedule is Owned, ISupplySchedule {
    using SafeDecimalMath for uint;
    using Math for uint;

    IKwenta public kwenta;
    IStakingRewards public stakingRewards;
    IMultipleMerkleDistributor public tradingRewards;

    // Time of the last inflation supply mint event
    uint public lastMintEvent;

    // Counter for number of weeks since the start of supply inflation
    uint public weekCounter;

    // The number of KWENTA rewarded to the caller of Kwenta.mint()
    uint public minterReward = 1e18;

    uint public constant INITIAL_SUPPLY = 313373e18;

    // Initial Supply * 240% Initial Inflation Rate / 52 weeks.
    uint public constant INITIAL_WEEKLY_SUPPLY = INITIAL_SUPPLY * 240 / 100 / 52;

    // Max KWENTA rewards for minter
    uint public constant MAX_MINTER_REWARD = 20 * 1e18;

    // How long each inflation period is before mint can be called
    uint public constant MINT_PERIOD_DURATION = 1 weeks;

    uint public immutable inflationStartDate;
    uint public constant MINT_BUFFER = 1 days;
    uint8 public constant SUPPLY_DECAY_START = 2; // Supply decay starts on the 2nd week of rewards
    uint8 public constant SUPPLY_DECAY_END = 208; // Inclusive of SUPPLY_DECAY_END week.

    // Weekly percentage decay of inflationary supply
    uint public constant DECAY_RATE = 20500000000000000; // 2.05% weekly

    // Percentage growth of terminal supply per annum
    uint public constant TERMINAL_SUPPLY_RATE_ANNUAL = 10000000000000000; // 1.0% pa

    uint public treasuryDiversion = 2000; // 20% to treasury
    uint public tradingRewardsDiversion = 2000;

    // notice treasury address may change
    address public treasuryDAO;

    /* ========== EVENTS ========== */
    
    /**
     * @notice Emitted when the inflationary supply is minted
     **/
    event SupplyMinted(uint supplyMinted, uint numberOfWeeksIssued, uint lastMintEvent);

    /**
     * @notice Emitted when the KWENTA minter reward amount is updated
     **/
    event MinterRewardUpdated(uint newRewardAmount);

    /**
     * @notice Emitted when setKwenta is called changing the Kwenta Proxy address
     **/
    event KwentaUpdated(address newAddress);

    /**
     * @notice Emitted when treasury inflation share is changed
     **/
    event TreasuryDiversionUpdated(uint newPercentage);

    /**
     * @notice Emitted when trading rewards inflation share is changed
     **/
    event TradingRewardsDiversionUpdated(uint newPercentage);

    /**
     * @notice Emitted when StakingRewards is changed
     **/
    event StakingRewardsUpdated(address newAddress);

    /**
     * @notice Emitted when TradingRewards is changed
     **/
    event TradingRewardsUpdated(address newAddress);

    /**
     * @notice Emitted when treasuryDAO address is changed
     **/
    event TreasuryDAOSet(address treasuryDAO);

    constructor(
        address _owner,
        address _treasuryDAO
    ) Owned(_owner) {
        treasuryDAO = _treasuryDAO;

        inflationStartDate = block.timestamp; // inflation starts as soon as the contract is deployed.
        lastMintEvent = block.timestamp;
        weekCounter = 0;
    }

    // ========== VIEWS ==========

    /**
     * @return The amount of KWENTA mintable for the inflationary supply
     */
    function mintableSupply() override public view returns (uint) {
        uint totalAmount;

        if (!isMintable()) {
            return totalAmount;
        }

        uint remainingWeeksToMint = weeksSinceLastIssuance();

        uint currentWeek = weekCounter;

        // Calculate total mintable supply from exponential decay function
        // The decay function stops after week 208
        while (remainingWeeksToMint > 0) {
            currentWeek++;

            if (currentWeek < SUPPLY_DECAY_START) {
                // If current week is before supply decay we add initial supply to mintableSupply
                totalAmount = totalAmount + INITIAL_WEEKLY_SUPPLY;
                remainingWeeksToMint--;
            } else if (currentWeek <= SUPPLY_DECAY_END) {
                // if current week before supply decay ends we add the new supply for the week
                // diff between current week and (supply decay start week - 1)
                uint decayCount = currentWeek - (SUPPLY_DECAY_START - 1);

                totalAmount = totalAmount + tokenDecaySupplyForWeek(decayCount);
                remainingWeeksToMint--;
            } else {
                // Terminal supply is calculated on the total supply of Kwenta including any new supply
                // We can compound the remaining week's supply at the fixed terminal rate
                uint totalSupply = IERC20(kwenta).totalSupply();
                uint currentTotalSupply = totalSupply + totalAmount;

                totalAmount = totalAmount + terminalInflationSupply(currentTotalSupply, remainingWeeksToMint);
                remainingWeeksToMint = 0;
            }
        }

        return totalAmount;
    }

    /**
     * @return A unit amount of decaying inflationary supply from the INITIAL_WEEKLY_SUPPLY
     * @dev New token supply reduces by the decay rate each week calculated as supply = INITIAL_WEEKLY_SUPPLY * ()
     */
    function tokenDecaySupplyForWeek(uint counter) public pure returns (uint) {
        // Apply exponential decay function to number of weeks since
        // start of inflation smoothing to calculate diminishing supply for the week.
        uint effectiveDecay = (SafeDecimalMath.unit() - DECAY_RATE).powDecimal(counter);
        uint supplyForWeek = INITIAL_WEEKLY_SUPPLY.multiplyDecimal(effectiveDecay);

        return supplyForWeek;
    }

    /**
     * @return A unit amount of terminal inflation supply
     * @dev Weekly compound rate based on number of weeks
     */
    function terminalInflationSupply(uint totalSupply, uint numOfWeeks) public pure returns (uint) {
        // rate = (1 + weekly rate) ^ num of weeks
        uint effectiveCompoundRate = (SafeDecimalMath.unit() + (TERMINAL_SUPPLY_RATE_ANNUAL / 52)).powDecimal(numOfWeeks);

        // return Supply * (effectiveRate - 1) for extra supply to issue based on number of weeks
        return totalSupply.multiplyDecimal(effectiveCompoundRate - SafeDecimalMath.unit());
    }

    /**
     * @dev Take timeDiff in seconds (Dividend) and MINT_PERIOD_DURATION as (Divisor)
     * @return Calculate the numberOfWeeks since last mint rounded down to 1 week
     */
    function weeksSinceLastIssuance() public view returns (uint) {
        // Get weeks since lastMintEvent
        // If lastMintEvent not set or 0, then start from inflation start date.
        uint timeDiff = block.timestamp - lastMintEvent;
        return timeDiff / MINT_PERIOD_DURATION;
    }

    /**
     * @return boolean whether the MINT_PERIOD_DURATION (7 days)
     * has passed since the lastMintEvent.
     * */
    function isMintable() override public view returns (bool) {
        return block.timestamp - lastMintEvent > MINT_PERIOD_DURATION;
    }

    // ========== MUTATIVE FUNCTIONS ==========

    /**
     * @notice Record the mint event from Kwenta by incrementing the inflation
     * week counter for the number of weeks minted (probabaly always 1)
     * and store the time of the event.
     * @param supplyMinted the amount of KWENTA the total supply was inflated by.
     * */
    function recordMintEvent(uint supplyMinted) internal returns (bool) {
        uint numberOfWeeksIssued = weeksSinceLastIssuance();

        // add number of weeks minted to weekCounter
        weekCounter = weekCounter + numberOfWeeksIssued;

        // Update mint event to latest week issued (start date + number of weeks issued * seconds in week)
        // 1 day time buffer is added so inflation is minted after feePeriod closes
        lastMintEvent = inflationStartDate + (weekCounter * MINT_PERIOD_DURATION) + MINT_BUFFER;

        emit SupplyMinted(supplyMinted, numberOfWeeksIssued, lastMintEvent);
        return true;
    }

    /**
     * @notice Mints new inflationary supply weekly
     * New KWENTA is distributed between the minter, treasury, and StakingRewards contract
     * */
    function mint() override external {
        require(address(stakingRewards) != address(0), "Staking rewards not set");
        require(address(tradingRewards) != address(0), "Trading rewards not set");

        uint supplyToMint = mintableSupply();
        require(supplyToMint > 0, "No supply is mintable");

        // record minting event before mutation to token supply
        recordMintEvent(supplyToMint);

        uint amountToDistribute = supplyToMint - minterReward;
        uint amountToTreasury = amountToDistribute * treasuryDiversion / 10000;
        uint amountToTradingRewards = amountToDistribute * tradingRewardsDiversion / 10000;
        uint amountToStakingRewards = amountToDistribute - amountToTreasury - amountToTradingRewards;

        kwenta.mint(treasuryDAO, amountToTreasury);
        kwenta.mint(address(tradingRewards), amountToTradingRewards);
        kwenta.mint(address(stakingRewards), amountToStakingRewards);
        stakingRewards.notifyRewardAmount(amountToStakingRewards);
        kwenta.mint(msg.sender, minterReward);
    }

    // ========== SETTERS ========== */

    /**
     * @notice Set the Kwenta should it ever change.
     * SupplySchedule requires Kwenta address as it has the authority
     * to record mint event.
     * */
    function setKwenta(IKwenta _kwenta) external onlyOwner {
        require(address(_kwenta) != address(0), "Address cannot be 0");
        kwenta = _kwenta;
        emit KwentaUpdated(address(kwenta));
    }

    /**
     * @notice Sets the reward amount of KWENTA for the caller of the public
     * function Kwenta.mint().
     * This incentivises anyone to mint the inflationary supply and the mintr
     * Reward will be deducted from the inflationary supply and sent to the caller.
     * @param amount the amount of KWENTA to reward the minter.
     * */
    function setMinterReward(uint amount) external onlyOwner {
        require(amount <= MAX_MINTER_REWARD, "SupplySchedule: Reward cannot exceed max minter reward");
        minterReward = amount;
        emit MinterRewardUpdated(minterReward);
    }

    function setTreasuryDiversion(uint _treasuryDiversion) override external onlyOwner {
        require(_treasuryDiversion + tradingRewardsDiversion < 10000, "SupplySchedule: Cannot be more than 100%");
        treasuryDiversion = _treasuryDiversion;
        emit TreasuryDiversionUpdated(_treasuryDiversion);
    }

    function setTradingRewardsDiversion(uint _tradingRewardsDiversion) override external onlyOwner {
        require(_tradingRewardsDiversion + treasuryDiversion < 10000, "SupplySchedule: Cannot be more than 100%");
        tradingRewardsDiversion = _tradingRewardsDiversion;
        emit TradingRewardsDiversionUpdated(_tradingRewardsDiversion);
    }

    function setStakingRewards(address _stakingRewards) override external onlyOwner {
        require(_stakingRewards != address(0), "SupplySchedule: Invalid Address");
        stakingRewards = IStakingRewards(_stakingRewards);
        emit StakingRewardsUpdated(_stakingRewards);
    }

    function setTradingRewards(address _tradingRewards) override external onlyOwner {
        require(_tradingRewards != address(0), "SupplySchedule: Invalid Address");
        tradingRewards = IMultipleMerkleDistributor(_tradingRewards);
        emit TradingRewardsUpdated(_tradingRewards);
    }

    /// @notice set treasuryDAO address
    /// @dev only owner may change address
    function setTreasuryDAO(address _treasuryDAO) external onlyOwner {
        require(_treasuryDAO != address(0), "SupplySchedule: Zero Address");
        treasuryDAO = _treasuryDAO;
        emit TreasuryDAOSet(treasuryDAO);
    }
}

File 19 of 55 : SafeDecimalMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// https://docs.synthetix.io/contracts/source/libraries/safedecimalmath
library SafeDecimalMath {
    /* Number of decimal places in the representations. */
    uint8 public constant decimals = 18;
    uint8 public constant highPrecisionDecimals = 27;

    /* The number representing 1.0. */
    uint256 public constant UNIT = 10**uint256(decimals);

    /* The number representing 1.0 for higher fidelity numbers. */
    uint256 public constant PRECISE_UNIT = 10**uint256(highPrecisionDecimals);
    uint256 private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR =
        10**uint256(highPrecisionDecimals - decimals);

    /**
     * @return Provides an interface to UNIT.
     */
    function unit() external pure returns (uint256) {
        return UNIT;
    }

    /**
     * @return Provides an interface to PRECISE_UNIT.
     */
    function preciseUnit() external pure returns (uint256) {
        return PRECISE_UNIT;
    }

    /**
     * @return The result of multiplying x and y, interpreting the operands as fixed-point
     * decimals.
     *
     * @dev A unit factor is divided out after the product of x and y is evaluated,
     * so that product must be less than 2**256. As this is an integer division,
     * the internal division always rounds down. This helps save on gas. Rounding
     * is more expensive on gas.
     */
    function multiplyDecimal(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        return (x * y) / UNIT;
    }

    /**
     * @return The result of safely dividing x and y. The return value is a high
     * precision decimal.
     *
     * @dev y is divided after the product of x and the standard precision unit
     * is evaluated, so the product of x and UNIT must be less than 2**256. As
     * this is an integer division, the result is always rounded down.
     * This helps save on gas. Rounding is more expensive on gas.
     */
    function divideDecimal(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        /* Reintroduce the UNIT factor that will be divided out by y. */
        return (x * UNIT) / y;
    }

    /**
     * @dev Convert a standard decimal representation to a high precision one.
     */
    function decimalToPreciseDecimal(uint256 i)
        internal
        pure
        returns (uint256)
    {
        return i * UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR;
    }

    /**
     * @dev Convert a high precision decimal to a standard decimal representation.
     */
    function preciseDecimalToDecimal(uint256 i)
        internal
        pure
        returns (uint256)
    {
        uint256 quotientTimesTen = i /
            (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);

        if (quotientTimesTen % 10 >= 5) {
            quotientTimesTen += 10;
        }

        return quotientTimesTen / 10;
    }

    // Computes `a - b`, setting the value to 0 if b > a.
    function floorsub(uint256 a, uint256 b) internal pure returns (uint256) {
        return b >= a ? 0 : a - b;
    }
}

File 20 of 55 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Libraries
import "./SafeDecimalMath.sol";

// https://docs.synthetix.io/contracts/source/libraries/math
library Math {
    using SafeDecimalMath for uint;

    /**
     * @dev Uses "exponentiation by squaring" algorithm where cost is 0(logN)
     * vs 0(N) for naive repeated multiplication.
     * Calculates x^n with x as fixed-point and n as regular unsigned int.
     * Calculates to 18 digits of precision with SafeDecimalMath.unit()
     */
    function powDecimal(uint x, uint n) internal pure returns (uint) {
        // https://mpark.github.io/programming/2014/08/18/exponentiation-by-squaring/

        uint result = SafeDecimalMath.unit();
        while (n > 0) {
            if (n % 2 != 0) {
                result = result.multiplyDecimal(x);
            }
            x = x.multiplyDecimal(x);
            n /= 2;
        }
        return result;
    }
}

File 21 of 55 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.9.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

File 22 of 55 : IKwenta.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";

interface IKwenta is IERC20 {

    function mint(address account, uint amount) external;

    function burn(uint amount) external;

    function setSupplySchedule(address _supplySchedule) external;

}

File 23 of 55 : IMultipleMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

// Allows anyone to claim a token if they exist in a merkle root.
interface IMultipleMerkleDistributor {
    /// @notice data structure for aggregating multiple claims
    struct Claims {
        uint256 index;
        address account;
        uint256 amount;
        bytes32[] merkleProof;
        uint256 epoch;
    }

    /// @notice event is triggered whenever a call to `claim` succeeds
    event Claimed(
        uint256 index,
        address account,
        uint256 amount,
        uint256 epoch
    );

    /// @notice event is triggered whenever a new merkle root is added
    event MerkleRootAdded(uint256 epoch);

    /// @return escrow for tokens claimed
    function rewardEscrow() external view returns (address);

    /// @return token to be distributed (KWENTA)
    function token() external view returns (address);

    // @return the merkle root of the merkle tree containing account balances available to claim
    function merkleRoots(uint256) external view returns (bytes32);

    /// @notice determine if indexed claim has been claimed
    /// @param index: used for claim managment
    /// @param epoch: distribution index number
    /// @return true if indexed claim has been claimed
    function isClaimed(uint256 index, uint256 epoch)
        external
        view
        returns (bool);

    /// @notice attempt to claim as `account` and escrow KWENTA for `account`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    /// @param epoch: distribution index number
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        uint256 epoch
    ) external;

    /// @notice function that aggregates multiple claims
    /// @param claims: array of valid claims
    function claimMultiple(Claims[] calldata claims) external;
}

File 24 of 55 : MultipleMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "./utils/Owned.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./interfaces/IRewardEscrow.sol";
import "./interfaces/IMultipleMerkleDistributor.sol";

/// @title Kwenta MultipleMerkleDistributor
/// @author JaredBorders and JChiaramonte7
/// @notice Facilitates trading incentives distribution over multiple periods.
contract MultipleMerkleDistributor is IMultipleMerkleDistributor, Owned {
    /// @notice escrow for tokens claimed
    address public immutable override rewardEscrow;

    /// @notice token to be distributed (KWENTA)
    address public immutable override token;

    /// @notice an index that is incremented for each new merkle root
    uint256 public distributionEpoch;

    /// @notice an epoch to merkle root mapping
    /// of a merkle tree containing account balances available to claim
    mapping(uint256 => bytes32) public override merkleRoots;

    /// @notice an epoch to packed array of claimed booleans mapping
    mapping(uint256 => mapping(uint256 => uint256)) private claimedBitMaps;

    /// @notice set addresses for deployed rewardEscrow and KWENTA.
    /// Establish merkle root for verification
    /// @param _owner: designated owner of this contract
    /// @param _token: address of erc20 token to be distributed
    /// @param _rewardEscrow: address of kwenta escrow for tokens claimed
    constructor(
        address _owner,
        address _token,
        address _rewardEscrow
    ) Owned(_owner) {
        token = _token;
        rewardEscrow = _rewardEscrow;
    }

    /// @notice set new merkle root for new distribution epoch
    /// @dev calling this function will increment distributionEpoch
    /// @param _merkleRoot: new merkle root
    function newMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
        merkleRoots[distributionEpoch] = _merkleRoot;
        emit MerkleRootAdded(distributionEpoch);
        distributionEpoch++;
    }

    /// @notice determine if indexed claim has been claimed
    /// @param index: used for claim managment
    /// @param epoch: distribution index to check
    /// @return true if indexed claim has been claimed
    function isClaimed(uint256 index, uint256 epoch)
        public
        view
        override
        returns (bool)
    {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMaps[epoch][claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }

    /// @notice set claimed status for indexed claim to true
    /// @param index: used for claim managment
    /// @param epoch: distribution index to check
    function _setClaimed(uint256 index, uint256 epoch) private {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        claimedBitMaps[epoch][claimedWordIndex] =
            claimedBitMaps[epoch][claimedWordIndex] |
            (1 << claimedBitIndex);
    }

    /// @notice attempt to claim as `account` and escrow KWENTA for `account`
    /// @param index: used for merkle tree managment and verification
    /// @param account: address used for escrow entry
    /// @param amount: $KWENTA amount to be escrowed
    /// @param merkleProof: off-chain generated proof of merkle tree inclusion
    /// @param epoch: distribution index to check
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        uint256 epoch
    ) public override {
        require(
            !isClaimed(index, epoch),
            "MultipleMerkleDistributor: Drop already claimed."
        );

        // verify the merkle proof
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(
            MerkleProof.verify(merkleProof, merkleRoots[epoch], node),
            "MultipleMerkleDistributor: Invalid proof."
        );

        // mark it claimed and send the token to RewardEscrow
        _setClaimed(index, epoch);
        IERC20(token).approve(rewardEscrow, amount);
        IRewardEscrow(rewardEscrow).createEscrowEntry(
            account,
            amount,
            52 weeks
        );

        emit Claimed(index, account, amount, epoch);
    }

    /// @notice function that aggregates multiple claims
    /// @param claims: array of valid claims
    function claimMultiple(Claims[] calldata claims) external override {
        uint256 cacheLength = claims.length;
        for (uint256 i = 0; i < cacheLength; ) {
            claim(
                claims[i].index,
                claims[i].account,
                claims[i].amount,
                claims[i].merkleProof,
                claims[i].epoch
            );
            unchecked {
                i++;
            }
        }
    }
}

File 25 of 55 : RewardEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

// Inheritance
import "./utils/Owned.sol";
import "./interfaces/IRewardEscrow.sol";

// Libraries
import "./libraries/SafeDecimalMath.sol";

// Internal references
import "./interfaces/IERC20.sol";
import "./interfaces/IKwenta.sol";
import "./interfaces/IStakingRewards.sol";

contract RewardEscrow is Owned, IRewardEscrow {
    using SafeDecimalMath for uint;

    /* ========== CONSTANTS/IMMUTABLES ========== */

    /* Max escrow duration */
    uint public constant MAX_DURATION = 2 * 52 weeks; // Default max 2 years duration

    IKwenta private immutable kwenta;

    /* ========== STATE VARIABLES ========== */

    IStakingRewards public stakingRewards;

    mapping(address => mapping(uint256 => VestingEntries.VestingEntry)) public vestingSchedules;

    mapping(address => uint256[]) public accountVestingEntryIDs;

    // Counter for new vesting entry ids 
    uint256 public nextEntryId;

    // An account's total escrowed KWENTA balance to save recomputing this for fee extraction purposes
    mapping(address => uint256) override public totalEscrowedAccountBalance;

    // An account's total vested reward KWENTA 
    mapping(address => uint256) override public totalVestedAccountBalance;

    // The total remaining escrowed balance, for verifying the actual KWENTA balance of this contract against
    uint256 public totalEscrowedBalance;

    // notice treasury address may change
    address public treasuryDAO;

    /* ========== MODIFIERS ========== */
    modifier onlyStakingRewards() {
        require(msg.sender == address(stakingRewards), "Only the StakingRewards can perform this action");
        _;
    }

    /* ========== EVENTS ========== */
    event Vested(address indexed beneficiary, uint value);
    event VestingEntryCreated(address indexed beneficiary, uint value, uint duration, uint entryID);
    event StakingRewardsSet(address rewardEscrow);
    event TreasuryDAOSet(address treasuryDAO);

    /* ========== CONSTRUCTOR ========== */

    constructor(address _owner, address _kwenta) Owned(_owner) {
        nextEntryId = 1;

        // set the Kwenta contract address as we need to transfer KWENTA when the user vests
        kwenta = IKwenta(_kwenta);
    }

    /* ========== SETTERS ========== */

    /*
    * @notice Function used to define the StakingRewards to use
    */
    function setStakingRewards(address _stakingRewards) public onlyOwner {
        require(address(stakingRewards) == address(0), "Staking Rewards already set");
        stakingRewards = IStakingRewards(_stakingRewards);
        emit StakingRewardsSet(address(_stakingRewards));
    }

    /// @notice set treasuryDAO address
    /// @dev only owner may change address
    function setTreasuryDAO(address _treasuryDAO) external onlyOwner {
        require(_treasuryDAO != address(0), "RewardEscrow: Zero Address");
        treasuryDAO = _treasuryDAO;
        emit TreasuryDAOSet(treasuryDAO);
    }

    /* ========== VIEW FUNCTIONS ========== */

    /**
     * @notice helper function to return kwenta address
     */
    function getKwentaAddress() override external view returns (address) {
        return address(kwenta);
    }

    /**
     * @notice A simple alias to totalEscrowedAccountBalance: provides ERC20 balance integration.
     */
    function balanceOf(address account) override public view returns (uint) {
        return totalEscrowedAccountBalance[account];
    }

    /**
     * @notice The number of vesting dates in an account's schedule.
     */
    function numVestingEntries(address account) override external view returns (uint) {
        return accountVestingEntryIDs[account].length;
    }

    /**
     * @notice Get a particular schedule entry for an account.
     * @return endTime the vesting entry object 
     * @return escrowAmount rate per second emission.
     */
    function getVestingEntry(address account, uint256 entryID) override external view returns (uint64 endTime, uint256 escrowAmount, uint256 duration) {
        endTime = vestingSchedules[account][entryID].endTime;
        escrowAmount = vestingSchedules[account][entryID].escrowAmount;
        duration = vestingSchedules[account][entryID].duration;
    }

    function getVestingSchedules(
        address account,
        uint256 index,
        uint256 pageSize
    ) override external view returns (VestingEntries.VestingEntryWithID[] memory) {
        uint256 endIndex = index + pageSize;

        // If index starts after the endIndex return no results
        if (endIndex <= index) {
            return new VestingEntries.VestingEntryWithID[](0);
        }

        // If the page extends past the end of the accountVestingEntryIDs, truncate it.
        if (endIndex > accountVestingEntryIDs[account].length) {
            endIndex = accountVestingEntryIDs[account].length;
        }

        uint256 n = endIndex - index;
        VestingEntries.VestingEntryWithID[] memory vestingEntries = new VestingEntries.VestingEntryWithID[](n);
        for (uint256 i; i < n; i++) {
            uint256 entryID = accountVestingEntryIDs[account][i + index];

            VestingEntries.VestingEntry memory entry = vestingSchedules[account][entryID];

            vestingEntries[i] = VestingEntries.VestingEntryWithID({
                endTime: uint64(entry.endTime),
                escrowAmount: entry.escrowAmount,
                entryID: entryID
            });
        }
        return vestingEntries;
    }

    function getAccountVestingEntryIDs(
        address account,
        uint256 index,
        uint256 pageSize
    ) override external view returns (uint256[] memory) {
        uint256 endIndex = index + pageSize;

        // If the page extends past the end of the accountVestingEntryIDs, truncate it.
        if (endIndex > accountVestingEntryIDs[account].length) {
            endIndex = accountVestingEntryIDs[account].length;
        }
        if (endIndex <= index) {
            return new uint256[](0);
        }

        uint256 n = endIndex - index;
        uint256[] memory page = new uint256[](n);
        for (uint256 i; i < n; i++) {
            page[i] = accountVestingEntryIDs[account][i + index];
        }
        return page;
    }

    function getVestingQuantity(address account, uint256[] calldata entryIDs) override external view returns (uint total, uint totalFee) {
        for (uint i = 0; i < entryIDs.length; i++) {
            VestingEntries.VestingEntry memory entry = vestingSchedules[account][entryIDs[i]];

            /* Skip entry if escrowAmount == 0 */
            if (entry.escrowAmount != 0) {
                (uint256 quantity, uint256 fee) = _claimableAmount(entry);

                /* add quantity to total */
                total += quantity;
                totalFee += fee;
            }
        }
    }

    function getVestingEntryClaimable(address account, uint256 entryID) override external view returns (uint quantity, uint fee) {
        VestingEntries.VestingEntry memory entry = vestingSchedules[account][entryID];
        (quantity, fee) = _claimableAmount(entry);
    }

    function _claimableAmount(VestingEntries.VestingEntry memory _entry) internal view returns (uint256 quantity, uint256 fee) {
        uint256 escrowAmount = _entry.escrowAmount;

        if (escrowAmount != 0) {
            /* Full escrow amounts claimable if block.timestamp equal to or after entry endTime */
            if (block.timestamp >= _entry.endTime) {
                quantity = escrowAmount;
            } else {
                fee = _earlyVestFee(_entry);
                quantity = escrowAmount - fee;
            }
        }
    }

    function _earlyVestFee(VestingEntries.VestingEntry memory _entry) internal view returns (uint256 earlyVestFee) {
        uint timeUntilVest = _entry.endTime - block.timestamp;
        // Fee starts at 90% and falls linearly
        uint initialFee = _entry.escrowAmount * 9 / 10;
        earlyVestFee = initialFee * timeUntilVest / _entry.duration;
    }

    function _isEscrowStaked(address _account) internal view returns (bool) {
        return stakingRewards.escrowedBalanceOf(_account) > 0;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    /**
     * Vest escrowed amounts that are claimable
     * Allows users to vest their vesting entries based on msg.sender
     */

    function vest(uint256[] calldata entryIDs) override external {
        uint256 total;
        uint256 totalFee;
        for (uint i = 0; i < entryIDs.length; i++) {
            VestingEntries.VestingEntry storage entry = vestingSchedules[msg.sender][entryIDs[i]];

            /* Skip entry if escrowAmount == 0 already vested */
            if (entry.escrowAmount != 0) {
                (uint256 quantity, uint256 fee) = _claimableAmount(entry);

                /* update entry to remove escrowAmount */
                entry.escrowAmount = 0;

                /* add quantity to total */
                total += quantity;
                totalFee += fee;
            }
        }

        /* Transfer vested tokens. Will revert if total > totalEscrowedAccountBalance */
        if (total != 0) {
            // Withdraw staked escrowed kwenta if needed for reward
            if (_isEscrowStaked(msg.sender)) {
                uint totalWithFee = total + totalFee;
                uint unstakedEscrow = totalEscrowedAccountBalance[msg.sender] - stakingRewards.escrowedBalanceOf(msg.sender);
                if (totalWithFee > unstakedEscrow) {
                    uint amountToUnstake = totalWithFee - unstakedEscrow;
                    unstakeEscrow(amountToUnstake);
                }
            }

            // Send any fee to Treasury
            if (totalFee != 0) {
                _reduceAccountEscrowBalances(msg.sender, totalFee);
                require(
                    IKwenta(address(kwenta))
                        .transfer(treasuryDAO, totalFee), 
                        "RewardEscrow: Token Transfer Failed"
                );
            }

            // Transfer kwenta
            _transferVestedTokens(msg.sender, total);
        }
        
    }

    /**
     * @notice Create an escrow entry to lock KWENTA for a given duration in seconds
     * @dev This call expects that the depositor (msg.sender) has already approved the Reward escrow contract
     * to spend the the amount being escrowed.
     */
    function createEscrowEntry(
        address beneficiary,
        uint256 deposit,
        uint256 duration
    ) override external {
        require(beneficiary != address(0), "Cannot create escrow with address(0)");

        /* Transfer KWENTA from msg.sender */
        require(IERC20(kwenta).transferFrom(msg.sender, address(this), deposit), "Token transfer failed");

        /* Append vesting entry for the beneficiary address */
        _appendVestingEntry(beneficiary, deposit, duration);
    }

    /**
     * @notice Add a new vesting entry at a given time and quantity to an account's schedule.
     * @dev A call to this should accompany a previous successful call to kwenta.transfer(rewardEscrow, amount),
     * to ensure that when the funds are withdrawn, there is enough balance.
     * @param account The account to append a new vesting entry to.
     * @param quantity The quantity of KWENTA that will be escrowed.
     * @param duration The duration that KWENTA will be emitted.
     */
    function appendVestingEntry(
        address account,
        uint256 quantity,
        uint256 duration
    ) override external onlyStakingRewards {
        _appendVestingEntry(account, quantity, duration);
    }

    /**
     * @notice Stakes escrowed KWENTA.
     * @dev No tokens are transfered during this process, but the StakingRewards escrowed balance is updated.
     * @param _amount The amount of escrowed KWENTA to be staked.
     */
    function stakeEscrow(uint256 _amount) override external {
        require(_amount + stakingRewards.escrowedBalanceOf(msg.sender) <= totalEscrowedAccountBalance[msg.sender], "Insufficient unstaked escrow");
        stakingRewards.stakeEscrow(msg.sender, _amount);
    }

    /**
     * @notice Unstakes escrowed KWENTA.
     * @dev No tokens are transfered during this process, but the StakingRewards escrowed balance is updated.
     * @param _amount The amount of escrowed KWENTA to be unstaked.
     */
    function unstakeEscrow(uint256 _amount) override public {
        stakingRewards.unstakeEscrow(msg.sender, _amount);
    }

    /* Transfer vested tokens and update totalEscrowedAccountBalance, totalVestedAccountBalance */
    function _transferVestedTokens(address _account, uint256 _amount) internal {
        _reduceAccountEscrowBalances(_account, _amount);
        totalVestedAccountBalance[_account] += _amount;
        IERC20(address(kwenta)).transfer(_account, _amount);
        emit Vested(_account, _amount);
    }

    function _reduceAccountEscrowBalances(address _account, uint256 _amount) internal {
        // Reverts if amount being vested is greater than the account's existing totalEscrowedAccountBalance
        totalEscrowedBalance -= _amount;
        totalEscrowedAccountBalance[_account] -= _amount;
    }

    /* ========== INTERNALS ========== */

    function _appendVestingEntry(
        address account,
        uint256 quantity,
        uint256 duration
    ) internal {
        /* No empty or already-passed vesting entries allowed. */
        require(quantity != 0, "Quantity cannot be zero");
        require(duration > 0 && duration <= MAX_DURATION, "Cannot escrow with 0 duration OR above max_duration");

        /* There must be enough balance in the contract to provide for the vesting entry. */
        totalEscrowedBalance += quantity;

        require(
            totalEscrowedBalance <= IERC20(address(kwenta)).balanceOf(address(this)),
            "Must be enough balance in the contract to provide for the vesting entry"
        );

        /* Escrow the tokens for duration. */
        uint endTime = block.timestamp + duration;

        /* Add quantity to account's escrowed balance */
        totalEscrowedAccountBalance[account] += quantity;

        uint entryID = nextEntryId;
        vestingSchedules[account][entryID] = VestingEntries.VestingEntry({endTime: uint64(endTime), escrowAmount: quantity, duration: duration});

        accountVestingEntryIDs[account].push(entryID);

        /* Increment the next entry id. */
        nextEntryId++;

        emit VestingEntryCreated(account, quantity, duration, entryID);
    }
}

File 26 of 55 : Kwenta.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./utils/ERC20.sol";
import "./utils/Owned.sol";
import "./interfaces/ISupplySchedule.sol";
import "./interfaces/IKwenta.sol";

contract Kwenta is ERC20, Owned, IKwenta {
    /// @notice defines inflationary supply schedule,
    /// according to which the KWENTA inflationary supply is released
    ISupplySchedule public supplySchedule;

    modifier onlySupplySchedule() {
        require(
            msg.sender == address(supplySchedule),
            "Kwenta: Only SupplySchedule can perform this action"
        );
        _;
    }

    constructor(
        string memory name,
        string memory symbol,
        uint256 _initialSupply,
        address _owner,
        address _initialHolder
    ) ERC20(name, symbol) Owned(_owner) {
        _mint(_initialHolder, _initialSupply);
    }

    // Mints inflationary supply
    function mint(address account, uint256 amount)
        external
        override
        onlySupplySchedule
    {
        _mint(account, amount);
    }

    function burn(uint256 amount) external override {
        _burn(msg.sender, amount);
    }

    function setSupplySchedule(address _supplySchedule)
        external
        override
        onlyOwner
    {
        require(_supplySchedule != address(0), "Kwenta: Invalid Address");
        supplySchedule = ISupplySchedule(_supplySchedule);
    }
}

File 27 of 55 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../interfaces/IERC20.sol";
import "../interfaces/IERC20Metadata.sol";
import "../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 28 of 55 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 29 of 55 : Context.sol
// SPDX-License-Identifier: MIT

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;
    }
}

File 30 of 55 : vKwentaRedeemer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/IvKwentaRedeemer.sol";
import "./utils/ERC20.sol";

/// @title A redemption contract for Kwenta
/// @dev All vKwenta used for redemption is locked within this contract
contract vKwentaRedeemer is IvKwentaRedeemer {
    /// token to be burned
    address public immutable vToken;
    /// token to be redeemed
    address public immutable token;

    event Redeemed(address redeemer, uint256 redeemedAmount);

    constructor(address _vToken, address _token) {
        vToken = _vToken;
        token = _token;
    }

    /// Allows caller to redeem an equivalent amount of token for vToken
    /// @dev caller must approve this contract to spend vToken
    /// @notice vToken is locked within this contract prior to transfer of token
    function redeem() external override {
        uint256 vTokenBalance = IERC20(vToken).balanceOf(msg.sender);

        /// ensure valid balance
        require(vTokenBalance > 0, "vKwentaRedeemer: No balance to redeem");
        require(
            vTokenBalance <= IERC20(token).balanceOf(address(this)),
            "vKwentaRedeemer: Insufficient contract balance"
        );

        /// lock vToken in this contract
        require(
            IERC20(vToken).transferFrom(
                msg.sender,
                address(this),
                vTokenBalance
            ),
            "vKwentaRedeemer: vToken transfer failed"
        );

        /// transfer token
        require(
            IERC20(token).transfer(msg.sender, vTokenBalance),
            "vKwentaRedeemer: token transfer failed"
        );

        emit Redeemed(msg.sender, vTokenBalance);
    }
}

File 31 of 55 : IvKwentaRedeemer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IvKwentaRedeemer {
    
    function redeem() external;

}

File 32 of 55 : vKwenta.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./utils/ERC20.sol";

/// @notice Purpose of this contract was to mint vKwenta for the initial Aelin raise.
/// @dev This is a one time use contract and supply can never be increased.
contract vKwenta is ERC20 {
    constructor(
        string memory _name,
        string memory _symbol,
        address _beneficiary,
        uint256 _amount
    ) ERC20(_name, _symbol) {
        _mint(_beneficiary, _amount);
    }
}

File 33 of 55 : ControlL2MerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/ControlL2MerkleDistributor.sol";

contract $ControlL2MerkleDistributor is ControlL2MerkleDistributor {
    constructor(address _crossDomainMessengerAddr, address _merkleDistributorL2Address) ControlL2MerkleDistributor(_crossDomainMessengerAddr, _merkleDistributorL2Address) {}

    function $crossDomainMessengerAddr() external view returns (address) {
        return crossDomainMessengerAddr;
    }

    function $merkleDistributorL2Address() external view returns (address) {
        return merkleDistributorL2Address;
    }
}

File 34 of 55 : Kwenta.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/Kwenta.sol";

contract $Kwenta is Kwenta {
    constructor(string memory name, string memory symbol, uint256 _initialSupply, address _owner, address _initialHolder) Kwenta(name, symbol, _initialSupply, _owner, _initialHolder) {}

    function $_transfer(address sender,address recipient,uint256 amount) external {
        return super._transfer(sender,recipient,amount);
    }

    function $_mint(address account,uint256 amount) external {
        return super._mint(account,amount);
    }

    function $_burn(address account,uint256 amount) external {
        return super._burn(account,amount);
    }

    function $_approve(address owner,address spender,uint256 amount) external {
        return super._approve(owner,spender,amount);
    }

    function $_beforeTokenTransfer(address from,address to,uint256 amount) external {
        return super._beforeTokenTransfer(from,to,amount);
    }

    function $_afterTokenTransfer(address from,address to,uint256 amount) external {
        return super._afterTokenTransfer(from,to,amount);
    }

    function $_msgSender() external view returns (address) {
        return super._msgSender();
    }

    function $_msgData() external view returns (bytes memory) {
        return super._msgData();
    }
}

File 35 of 55 : MerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/MerkleDistributor.sol";

contract $MerkleDistributor is MerkleDistributor {
    constructor(address _owner, address _token, address _rewardEscrow, bytes32 _merkleRoot) MerkleDistributor(_owner, _token, _rewardEscrow, _merkleRoot) {}
}

File 36 of 55 : MultipleMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/MultipleMerkleDistributor.sol";

contract $MultipleMerkleDistributor is MultipleMerkleDistributor {
    constructor(address _owner, address _token, address _rewardEscrow) MultipleMerkleDistributor(_owner, _token, _rewardEscrow) {}
}

File 37 of 55 : RewardEscrow.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/RewardEscrow.sol";

contract $RewardEscrow is RewardEscrow {
    constructor(address _owner, address _kwenta) RewardEscrow(_owner, _kwenta) {}

    function $_claimableAmount(VestingEntries.VestingEntry calldata _entry) external view returns (uint256, uint256) {
        return super._claimableAmount(_entry);
    }

    function $_earlyVestFee(VestingEntries.VestingEntry calldata _entry) external view returns (uint256) {
        return super._earlyVestFee(_entry);
    }

    function $_isEscrowStaked(address _account) external view returns (bool) {
        return super._isEscrowStaked(_account);
    }

    function $_transferVestedTokens(address _account,uint256 _amount) external {
        return super._transferVestedTokens(_account,_amount);
    }

    function $_reduceAccountEscrowBalances(address _account,uint256 _amount) external {
        return super._reduceAccountEscrowBalances(_account,_amount);
    }

    function $_appendVestingEntry(address account,uint256 quantity,uint256 duration) external {
        return super._appendVestingEntry(account,quantity,duration);
    }
}

File 38 of 55 : StakingRewards.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/StakingRewards.sol";

contract $StakingRewards is StakingRewards {
    constructor(address _token, address _rewardEscrow, address _supplySchedule) StakingRewards(_token, _rewardEscrow, _supplySchedule) {}

    function $_pause() external {
        return super._pause();
    }

    function $_unpause() external {
        return super._unpause();
    }

    function $_msgSender() external view returns (address) {
        return super._msgSender();
    }

    function $_msgData() external view returns (bytes memory) {
        return super._msgData();
    }
}

File 39 of 55 : SupplySchedule.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/SupplySchedule.sol";

contract $SupplySchedule is SupplySchedule {
    constructor(address _owner, address _treasuryDAO) SupplySchedule(_owner, _treasuryDAO) {}

    function $recordMintEvent(uint256 supplyMinted) external returns (bool) {
        return super.recordMintEvent(supplyMinted);
    }
}

File 40 of 55 : IControlL2MerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IControlL2MerkleDistributor.sol";

abstract contract $IControlL2MerkleDistributor is IControlL2MerkleDistributor {
    constructor() {}
}

File 41 of 55 : IERC20.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IERC20.sol";

abstract contract $IERC20 is IERC20 {
    constructor() {}
}

File 42 of 55 : IERC20Metadata.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IERC20Metadata.sol";

abstract contract $IERC20Metadata is IERC20Metadata {
    constructor() {}
}

File 43 of 55 : IKwenta.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IKwenta.sol";

abstract contract $IKwenta is IKwenta {
    constructor() {}
}

File 44 of 55 : IMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IMerkleDistributor.sol";

abstract contract $IMerkleDistributor is IMerkleDistributor {
    constructor() {}
}

File 45 of 55 : IMultipleMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IMultipleMerkleDistributor.sol";

abstract contract $IMultipleMerkleDistributor is IMultipleMerkleDistributor {
    constructor() {}
}

File 46 of 55 : IRewardEscrow.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IRewardEscrow.sol";

contract $VestingEntries {
    constructor() {}
}

abstract contract $IRewardEscrow is IRewardEscrow {
    constructor() {}
}

File 47 of 55 : IStakingRewards.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IStakingRewards.sol";

abstract contract $IStakingRewards is IStakingRewards {
    constructor() {}
}

File 48 of 55 : ISupplySchedule.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/ISupplySchedule.sol";

abstract contract $ISupplySchedule is ISupplySchedule {
    constructor() {}
}

File 49 of 55 : IvKwentaRedeemer.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/interfaces/IvKwentaRedeemer.sol";

abstract contract $IvKwentaRedeemer is IvKwentaRedeemer {
    constructor() {}
}

File 50 of 55 : Math.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/libraries/Math.sol";

contract $Math {
    constructor() {}

    function $powDecimal(uint256 x,uint256 n) external pure returns (uint256) {
        return Math.powDecimal(x,n);
    }
}

File 51 of 55 : Context.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

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

contract $Context is Context {
    constructor() {}

    function $_msgSender() external view returns (address) {
        return super._msgSender();
    }

    function $_msgData() external view returns (bytes memory) {
        return super._msgData();
    }
}

File 52 of 55 : ERC20.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/utils/ERC20.sol";

contract $ERC20 is ERC20 {
    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}

    function $_transfer(address sender,address recipient,uint256 amount) external {
        return super._transfer(sender,recipient,amount);
    }

    function $_mint(address account,uint256 amount) external {
        return super._mint(account,amount);
    }

    function $_burn(address account,uint256 amount) external {
        return super._burn(account,amount);
    }

    function $_approve(address owner,address spender,uint256 amount) external {
        return super._approve(owner,spender,amount);
    }

    function $_beforeTokenTransfer(address from,address to,uint256 amount) external {
        return super._beforeTokenTransfer(from,to,amount);
    }

    function $_afterTokenTransfer(address from,address to,uint256 amount) external {
        return super._afterTokenTransfer(from,to,amount);
    }

    function $_msgSender() external view returns (address) {
        return super._msgSender();
    }

    function $_msgData() external view returns (bytes memory) {
        return super._msgData();
    }
}

File 53 of 55 : Owned.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../../contracts/utils/Owned.sol";

contract $Owned is Owned {
    constructor(address _owner) Owned(_owner) {}
}

File 54 of 55 : vKwenta.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/vKwenta.sol";

contract $vKwenta is vKwenta {
    constructor(string memory _name, string memory _symbol, address _beneficiary, uint256 _amount) vKwenta(_name, _symbol, _beneficiary, _amount) {}

    function $_transfer(address sender,address recipient,uint256 amount) external {
        return super._transfer(sender,recipient,amount);
    }

    function $_mint(address account,uint256 amount) external {
        return super._mint(account,amount);
    }

    function $_burn(address account,uint256 amount) external {
        return super._burn(account,amount);
    }

    function $_approve(address owner,address spender,uint256 amount) external {
        return super._approve(owner,spender,amount);
    }

    function $_beforeTokenTransfer(address from,address to,uint256 amount) external {
        return super._beforeTokenTransfer(from,to,amount);
    }

    function $_afterTokenTransfer(address from,address to,uint256 amount) external {
        return super._afterTokenTransfer(from,to,amount);
    }

    function $_msgSender() external view returns (address) {
        return super._msgSender();
    }

    function $_msgData() external view returns (bytes memory) {
        return super._msgData();
    }
}

File 55 of 55 : vKwentaRedeemer.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0;

import "../contracts/vKwentaRedeemer.sol";

contract $vKwentaRedeemer is vKwentaRedeemer {
    constructor(address _vToken, address _token) vKwentaRedeemer(_vToken, _token) {}
}

Settings
{
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/libraries/SafeDecimalMath.sol": {
      "SafeDecimalMath": "0x68a356240deaec81d6d59986c2cde4b8035ddeaf"
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_rewardEscrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"MerkleRootAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"internalType":"struct IMultipleMerkleDistributor.Claims[]","name":"claims","type":"tuple[]"}],"name":"claimMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributionEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"isClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"merkleRoots","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"newMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardEscrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60c06040523480156200001157600080fd5b506040516200197e3803806200197e8339818101604052810190620000379190620001b6565b82600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620000ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000a29062000277565b60405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c6000826040516200011f9291906200024a565b60405180910390a1508173ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff1660601b815250508073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1660601b8152505050505062000326565b600081519050620001b0816200030c565b92915050565b600080600060608486031215620001d257620001d1620002de565b5b6000620001e2868287016200019f565b9350506020620001f5868287016200019f565b925050604062000208868287016200019f565b9150509250925092565b6200021d81620002aa565b82525050565b60006200023260198362000299565b91506200023f82620002e3565b602082019050919050565b600060408201905062000261600083018562000212565b62000270602083018462000212565b9392505050565b60006020820190508181036000830152620002928162000223565b9050919050565b600082825260208201905092915050565b6000620002b782620002be565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600080fd5b7f4f776e657220616464726573732063616e6e6f74206265203000000000000000600082015250565b6200031781620002aa565b81146200032357600080fd5b50565b60805160601c60a05160601c611617620003676000396000818161079601526109a501526000818161053d015281816107d2015261086401526116176000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806393503099116100715780639350309914610169578063a430be6c14610185578063ab5943db146101a3578063c02bb7ba146101bf578063f364c90c146101db578063fc0c546a1461020b576100b4565b80630b09d515146100b95780631627540c146100d757806353a47bb7146100f357806371c5ecb11461011157806379ba5097146101415780638da5cb5b1461014b575b600080fd5b6100c1610229565b6040516100ce9190611144565b60405180910390f35b6100f160048036038101906100ec9190610c8a565b61022f565b005b6100fb6102b2565b6040516101089190610fea565b60405180910390f35b61012b60048036038101906101269190610d5e565b6102d8565b60405161013891906110a9565b60405180910390f35b6101496102f0565b005b6101536104a1565b6040516101609190610fea565b60405180910390f35b610183600480360381019061017e9190610d31565b6104c5565b005b61018d61053b565b60405161019a9190610fea565b60405180910390f35b6101bd60048036038101906101b89190610cb7565b61055f565b005b6101d960048036038101906101d49190610d8b565b610673565b005b6101f560048036038101906101f09190610e25565b61093b565b604051610202919061108e565b60405180910390f35b6102136109a3565b6040516102209190610fea565b60405180910390f35b60025481565b6102376109c7565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22816040516102a79190610fea565b60405180910390a150565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60036020528060005260406000206000915090505481565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610380576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610377906110c4565b60405180910390fd5b7fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040516103f3929190611005565b60405180910390a1600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6104cd6109c7565b80600360006002548152602001908152602001600020819055507ffa7a4543b160b1709fbf83816c0a0ea72a11709913c70902c457f0030af120e56002546040516105189190611144565b60405180910390a160026000815480929190610533906112d5565b919050555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600082829050905060005b8181101561066d57610660848483818110610588576105876113e5565b5b905060200281019061059a9190611207565b600001358585848181106105b1576105b06113e5565b5b90506020028101906105c39190611207565b60200160208101906105d59190610c8a565b8686858181106105e8576105e76113e5565b5b90506020028101906105fa9190611207565b60400135878786818110610611576106106113e5565b5b90506020028101906106239190611207565b806060019061063291906111a4565b898988818110610645576106446113e5565b5b90506020028101906106579190611207565b60800135610673565b808060010191505061056a565b50505050565b61067d868261093b565b156106bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106b4906110e4565b60405180910390fd5b60008686866040516020016106d493929190610fad565b60405160208183030381529060405280519060200120905061074b848480806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050600360008581526020019081526020016000205483610a57565b61078a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078190611124565b60405180910390fd5b6107948783610b0d565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663095ea7b37f0000000000000000000000000000000000000000000000000000000000000000876040518363ffffffff1660e01b815260040161080f92919061102e565b602060405180830381600087803b15801561082957600080fd5b505af115801561083d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108619190610d04565b507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a0416ed387876301dfe2006040518463ffffffff1660e01b81526004016108c393929190611057565b600060405180830381600087803b1580156108dd57600080fd5b505af11580156108f1573d6000803e3d6000fd5b505050507fd9cb1e2714d65a111c0f20f060176ad657496bd47a3de04ec7c3d4ca232112ac8787878560405161092a949392919061115f565b60405180910390a150505050505050565b6000806101008461094c9190611240565b905060006101008561095e9190611356565b905060006004600086815260200190815260200160002060008481526020019081526020016000205490506000826001901b9050808183161494505050505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a4c90611104565b60405180910390fd5b565b60008082905060005b8551811015610aff576000868281518110610a7e57610a7d6113e5565b5b60200260200101519050808311610abf578281604051602001610aa2929190610f81565b604051602081830303815290604052805190602001209250610aeb565b8083604051602001610ad2929190610f81565b6040516020818303038152906040528051906020012092505b508080610af7906112d5565b915050610a60565b508381149150509392505050565b600061010083610b1d9190611240565b9050600061010084610b2f9190611356565b9050806001901b60046000858152602001908152602001600020600084815260200190815260200160002054176004600085815260200190815260200160002060008481526020019081526020016000208190555050505050565b600081359050610b9981611585565b92915050565b60008083601f840112610bb557610bb4611419565b5b8235905067ffffffffffffffff811115610bd257610bd1611414565b5b602083019150836020820283011115610bee57610bed611428565b5b9250929050565b60008083601f840112610c0b57610c0a611419565b5b8235905067ffffffffffffffff811115610c2857610c27611414565b5b602083019150836020820283011115610c4457610c43611428565b5b9250929050565b600081519050610c5a8161159c565b92915050565b600081359050610c6f816115b3565b92915050565b600081359050610c84816115ca565b92915050565b600060208284031215610ca057610c9f611437565b5b6000610cae84828501610b8a565b91505092915050565b60008060208385031215610cce57610ccd611437565b5b600083013567ffffffffffffffff811115610cec57610ceb611432565b5b610cf885828601610bf5565b92509250509250929050565b600060208284031215610d1a57610d19611437565b5b6000610d2884828501610c4b565b91505092915050565b600060208284031215610d4757610d46611437565b5b6000610d5584828501610c60565b91505092915050565b600060208284031215610d7457610d73611437565b5b6000610d8284828501610c75565b91505092915050565b60008060008060008060a08789031215610da857610da7611437565b5b6000610db689828a01610c75565b9650506020610dc789828a01610b8a565b9550506040610dd889828a01610c75565b945050606087013567ffffffffffffffff811115610df957610df8611432565b5b610e0589828a01610b9f565b93509350506080610e1889828a01610c75565b9150509295509295509295565b60008060408385031215610e3c57610e3b611437565b5b6000610e4a85828601610c75565b9250506020610e5b85828601610c75565b9150509250929050565b610e6e81611271565b82525050565b610e85610e8082611271565b61131e565b82525050565b610e9481611283565b82525050565b610ea38161128f565b82525050565b610eba610eb58261128f565b611330565b82525050565b610ec9816112c3565b82525050565b6000610edc60358361122f565b9150610ee782611449565b604082019050919050565b6000610eff60308361122f565b9150610f0a82611498565b604082019050919050565b6000610f22602f8361122f565b9150610f2d826114e7565b604082019050919050565b6000610f4560298361122f565b9150610f5082611536565b604082019050919050565b610f64816112b9565b82525050565b610f7b610f76826112b9565b61134c565b82525050565b6000610f8d8285610ea9565b602082019150610f9d8284610ea9565b6020820191508190509392505050565b6000610fb98286610f6a565b602082019150610fc98285610e74565b601482019150610fd98284610f6a565b602082019150819050949350505050565b6000602082019050610fff6000830184610e65565b92915050565b600060408201905061101a6000830185610e65565b6110276020830184610e65565b9392505050565b60006040820190506110436000830185610e65565b6110506020830184610f5b565b9392505050565b600060608201905061106c6000830186610e65565b6110796020830185610f5b565b6110866040830184610ec0565b949350505050565b60006020820190506110a36000830184610e8b565b92915050565b60006020820190506110be6000830184610e9a565b92915050565b600060208201905081810360008301526110dd81610ecf565b9050919050565b600060208201905081810360008301526110fd81610ef2565b9050919050565b6000602082019050818103600083015261111d81610f15565b9050919050565b6000602082019050818103600083015261113d81610f38565b9050919050565b60006020820190506111596000830184610f5b565b92915050565b60006080820190506111746000830187610f5b565b6111816020830186610e65565b61118e6040830185610f5b565b61119b6060830184610f5b565b95945050505050565b600080833560016020038436030381126111c1576111c0611423565b5b80840192508235915067ffffffffffffffff8211156111e3576111e261141e565b5b6020830192506020820236038313156111ff576111fe61142d565b5b509250929050565b60008235600160a00383360303811261122357611222611423565b5b80830191505092915050565b600082825260208201905092915050565b600061124b826112b9565b9150611256836112b9565b925082611266576112656113b6565b5b828204905092915050565b600061127c82611299565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006112ce826112b9565b9050919050565b60006112e0826112b9565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561131357611312611387565b5b600182019050919050565b60006113298261133a565b9050919050565b6000819050919050565b60006113458261143c565b9050919050565b6000819050919050565b6000611361826112b9565b915061136c836112b9565b92508261137c5761137b6113b6565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008160601b9050919050565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560008201527f2063616e20616363657074206f776e6572736869700000000000000000000000602082015250565b7f4d756c7469706c654d65726b6c654469737472696275746f723a2044726f702060008201527f616c726561647920636c61696d65642e00000000000000000000000000000000602082015250565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660008201527f6f726d207468697320616374696f6e0000000000000000000000000000000000602082015250565b7f4d756c7469706c654d65726b6c654469737472696275746f723a20496e76616c60008201527f69642070726f6f662e0000000000000000000000000000000000000000000000602082015250565b61158e81611271565b811461159957600080fd5b50565b6115a581611283565b81146115b057600080fd5b50565b6115bc8161128f565b81146115c757600080fd5b50565b6115d3816112b9565b81146115de57600080fd5b5056fea2646970667358221220985af8a396f664e37df99f30185f939cb6de09ef6eea25dc5635c1e260630b7e64736f6c63430008070033000000000000000000000000652c46a302060b324a02d2d3e4a56e3da07fa91b000000000000000000000000da0c33402fc1e10d18c532f0ed9c1a6c5c9e386c000000000000000000000000afd87d1a62260bd5714c55a1bb4057bdc8dfa413

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000652c46a302060b324a02d2d3e4a56e3da07fa91b000000000000000000000000da0c33402fc1e10d18c532f0ed9c1a6c5c9e386c000000000000000000000000afd87d1a62260bd5714c55a1bb4057bdc8dfa413

-----Decoded View---------------
Arg [0] : _owner (address): 0x652c46a302060B324A02d2d3e4a56e3DA07FA91b
Arg [1] : _token (address): 0xDA0C33402Fc1e10d18c532F0Ed9c1A6c5C9e386C
Arg [2] : _rewardEscrow (address): 0xaFD87d1a62260bD5714C55a1BB4057bDc8dFA413

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000652c46a302060b324a02d2d3e4a56e3da07fa91b
Arg [1] : 000000000000000000000000da0c33402fc1e10d18c532f0ed9c1a6c5c9e386c
Arg [2] : 000000000000000000000000afd87d1a62260bd5714c55a1bb4057bdc8dfa413


Deployed ByteCode Sourcemap

480:4497:34:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;818:32;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;382:138:52;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;166:29;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;977:55:34;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;526:266:52;;;:::i;:::-;;140:20;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;1856:202:34;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;600:46;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;4531:444;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;3515:908;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;2276:384;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;702:39;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;818:32;;;;:::o;382:138:52:-;827:12;:10;:12::i;:::-;470:6:::1;453:14;;:23;;;;;;;;;;;;;;;;;;491:22;506:6;491:22;;;;;;:::i;:::-;;;;;;;;382:138:::0;:::o;166:29::-;;;;;;;;;;;;;:::o;977:55:34:-;;;;;;;;;;;;;;;;;:::o;526:266:52:-;594:14;;;;;;;;;;;580:28;;:10;:28;;;572:94;;;;;;;;;;;;:::i;:::-;;;;;;;;;681:35;694:5;;;;;;;;;;701:14;;;;;;;;;;;681:35;;;;;;;:::i;:::-;;;;;;;;734:14;;;;;;;;;;;726:5;;:22;;;;;;;;;;;;;;;;;;783:1;758:14;;:27;;;;;;;;;;;;;;;;;;526:266::o;140:20::-;;;;;;;;;;;;:::o;1856:202:34:-;827:12:52;:10;:12::i;:::-;1962:11:34::1;1929;:30;1941:17;;1929:30;;;;;;;;;;;:44;;;;1988:34;2004:17;;1988:34;;;;;;:::i;:::-;;;;;;;;2032:17;;:19;;;;;;;;;:::i;:::-;;;;;;1856:202:::0;:::o;600:46::-;;;:::o;4531:444::-;4608:19;4630:6;;:13;;4608:35;;4658:9;4653:316;4677:11;4673:1;:15;4653:316;;;4706:193;4729:6;;4736:1;4729:9;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;:15;;;4762:6;;4769:1;4762:9;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;:17;;;;;;;;;;:::i;:::-;4797:6;;4804:1;4797:9;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;:16;;;4831:6;;4838:1;4831:9;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;:21;;;;;;;;:::i;:::-;4870:6;;4877:1;4870:9;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;:15;;;4706:5;:193::i;:::-;4941:3;;;;;;;4653:316;;;;4598:377;4531:444;;:::o;3515:908::-;3719:23;3729:5;3736;3719:9;:23::i;:::-;3718:24;3697:119;;;;;;;;;;;;:::i;:::-;;;;;;;;;3862:12;3904:5;3911:7;3920:6;3887:40;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3877:51;;;;;;3862:66;;3959:57;3978:11;;3959:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3991:11;:18;4003:5;3991:18;;;;;;;;;;;;4011:4;3959:18;:57::i;:::-;3938:145;;;;;;;;;;;;:::i;:::-;;;;;;;;;4156:25;4168:5;4175;4156:11;:25::i;:::-;4198:5;4191:21;;;4213:12;4227:6;4191:43;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;4258:12;4244:45;;;4303:7;4324:6;4344:8;4244:118;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4378:38;4386:5;4393:7;4402:6;4410:5;4378:38;;;;;;;;;:::i;:::-;;;;;;;;3687:736;3515:908;;;;;;:::o;2276:384::-;2387:4;2407:24;2442:3;2434:5;:11;;;;:::i;:::-;2407:38;;2455:23;2489:3;2481:5;:11;;;;:::i;:::-;2455:37;;2502:19;2524:14;:21;2539:5;2524:21;;;;;;;;;;;:39;2546:16;2524:39;;;;;;;;;;;;2502:61;;2573:12;2594:15;2589:1;:20;;2573:37;;2649:4;2641;2627:11;:18;:26;2620:33;;;;;;2276:384;;;;:::o;702:39::-;;;:::o;863:131:52:-;930:5;;;;;;;;;;916:19;;:10;:19;;;908:79;;;;;;;;;;;;:::i;:::-;;;;;;;;;863:131::o;777:809:7:-;898:4;914:20;937:4;914:27;;957:9;952:515;976:5;:12;972:1;:16;952:515;;;1009:20;1032:5;1038:1;1032:8;;;;;;;;:::i;:::-;;;;;;;;1009:31;;1075:12;1059;:28;1055:402;;1227:12;1241;1210:44;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1200:55;;;;;;1185:70;;1055:402;;;1414:12;1428;1397:44;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1387:55;;;;;;1372:70;;1055:402;995:472;990:3;;;;;:::i;:::-;;;;952:515;;;;1575:4;1559:12;:20;1552:27;;;777:809;;;;;:::o;2824:301:34:-;2893:24;2928:3;2920:5;:11;;;;:::i;:::-;2893:38;;2941:23;2975:3;2967:5;:11;;;;:::i;:::-;2941:37;;3102:15;3097:1;:20;;3042:14;:21;3057:5;3042:21;;;;;;;;;;;:39;3064:16;3042:39;;;;;;;;;;;;:76;2988:14;:21;3003:5;2988:21;;;;;;;;;;;:39;3010:16;2988:39;;;;;;;;;;;:130;;;;2883:242;;2824:301;;:::o;7:139:55:-;53:5;91:6;78:20;69:29;;107:33;134:5;107:33;:::i;:::-;7:139;;;;:::o;169:568::-;242:8;252:6;302:3;295:4;287:6;283:17;279:27;269:122;;310:79;;:::i;:::-;269:122;423:6;410:20;400:30;;453:18;445:6;442:30;439:117;;;475:79;;:::i;:::-;439:117;589:4;581:6;577:17;565:29;;643:3;635:4;627:6;623:17;613:8;609:32;606:41;603:128;;;650:79;;:::i;:::-;603:128;169:568;;;;;:::o;793:594::-;892:8;902:6;952:3;945:4;937:6;933:17;929:27;919:122;;960:79;;:::i;:::-;919:122;1073:6;1060:20;1050:30;;1103:18;1095:6;1092:30;1089:117;;;1125:79;;:::i;:::-;1089:117;1239:4;1231:6;1227:17;1215:29;;1293:3;1285:4;1277:6;1273:17;1263:8;1259:32;1256:41;1253:128;;;1300:79;;:::i;:::-;1253:128;793:594;;;;;:::o;1393:137::-;1447:5;1478:6;1472:13;1463:22;;1494:30;1518:5;1494:30;:::i;:::-;1393:137;;;;:::o;1536:139::-;1582:5;1620:6;1607:20;1598:29;;1636:33;1663:5;1636:33;:::i;:::-;1536:139;;;;:::o;1681:::-;1727:5;1765:6;1752:20;1743:29;;1781:33;1808:5;1781:33;:::i;:::-;1681:139;;;;:::o;1826:329::-;1885:6;1934:2;1922:9;1913:7;1909:23;1905:32;1902:119;;;1940:79;;:::i;:::-;1902:119;2060:1;2085:53;2130:7;2121:6;2110:9;2106:22;2085:53;:::i;:::-;2075:63;;2031:117;1826:329;;;;:::o;2161:611::-;2273:6;2281;2330:2;2318:9;2309:7;2305:23;2301:32;2298:119;;;2336:79;;:::i;:::-;2298:119;2484:1;2473:9;2469:17;2456:31;2514:18;2506:6;2503:30;2500:117;;;2536:79;;:::i;:::-;2500:117;2649:106;2747:7;2738:6;2727:9;2723:22;2649:106;:::i;:::-;2631:124;;;;2427:338;2161:611;;;;;:::o;2778:345::-;2845:6;2894:2;2882:9;2873:7;2869:23;2865:32;2862:119;;;2900:79;;:::i;:::-;2862:119;3020:1;3045:61;3098:7;3089:6;3078:9;3074:22;3045:61;:::i;:::-;3035:71;;2991:125;2778:345;;;;:::o;3129:329::-;3188:6;3237:2;3225:9;3216:7;3212:23;3208:32;3205:119;;;3243:79;;:::i;:::-;3205:119;3363:1;3388:53;3433:7;3424:6;3413:9;3409:22;3388:53;:::i;:::-;3378:63;;3334:117;3129:329;;;;:::o;3464:::-;3523:6;3572:2;3560:9;3551:7;3547:23;3543:32;3540:119;;;3578:79;;:::i;:::-;3540:119;3698:1;3723:53;3768:7;3759:6;3748:9;3744:22;3723:53;:::i;:::-;3713:63;;3669:117;3464:329;;;;:::o;3799:1141::-;3921:6;3929;3937;3945;3953;3961;4010:3;3998:9;3989:7;3985:23;3981:33;3978:120;;;4017:79;;:::i;:::-;3978:120;4137:1;4162:53;4207:7;4198:6;4187:9;4183:22;4162:53;:::i;:::-;4152:63;;4108:117;4264:2;4290:53;4335:7;4326:6;4315:9;4311:22;4290:53;:::i;:::-;4280:63;;4235:118;4392:2;4418:53;4463:7;4454:6;4443:9;4439:22;4418:53;:::i;:::-;4408:63;;4363:118;4548:2;4537:9;4533:18;4520:32;4579:18;4571:6;4568:30;4565:117;;;4601:79;;:::i;:::-;4565:117;4714:80;4786:7;4777:6;4766:9;4762:22;4714:80;:::i;:::-;4696:98;;;;4491:313;4843:3;4870:53;4915:7;4906:6;4895:9;4891:22;4870:53;:::i;:::-;4860:63;;4814:119;3799:1141;;;;;;;;:::o;4946:474::-;5014:6;5022;5071:2;5059:9;5050:7;5046:23;5042:32;5039:119;;;5077:79;;:::i;:::-;5039:119;5197:1;5222:53;5267:7;5258:6;5247:9;5243:22;5222:53;:::i;:::-;5212:63;;5168:117;5324:2;5350:53;5395:7;5386:6;5375:9;5371:22;5350:53;:::i;:::-;5340:63;;5295:118;4946:474;;;;;:::o;5426:118::-;5513:24;5531:5;5513:24;:::i;:::-;5508:3;5501:37;5426:118;;:::o;5550:157::-;5655:45;5675:24;5693:5;5675:24;:::i;:::-;5655:45;:::i;:::-;5650:3;5643:58;5550:157;;:::o;5713:109::-;5794:21;5809:5;5794:21;:::i;:::-;5789:3;5782:34;5713:109;;:::o;5828:118::-;5915:24;5933:5;5915:24;:::i;:::-;5910:3;5903:37;5828:118;;:::o;5952:157::-;6057:45;6077:24;6095:5;6077:24;:::i;:::-;6057:45;:::i;:::-;6052:3;6045:58;5952:157;;:::o;6115:161::-;6217:52;6263:5;6217:52;:::i;:::-;6212:3;6205:65;6115:161;;:::o;6282:366::-;6424:3;6445:67;6509:2;6504:3;6445:67;:::i;:::-;6438:74;;6521:93;6610:3;6521:93;:::i;:::-;6639:2;6634:3;6630:12;6623:19;;6282:366;;;:::o;6654:::-;6796:3;6817:67;6881:2;6876:3;6817:67;:::i;:::-;6810:74;;6893:93;6982:3;6893:93;:::i;:::-;7011:2;7006:3;7002:12;6995:19;;6654:366;;;:::o;7026:::-;7168:3;7189:67;7253:2;7248:3;7189:67;:::i;:::-;7182:74;;7265:93;7354:3;7265:93;:::i;:::-;7383:2;7378:3;7374:12;7367:19;;7026:366;;;:::o;7398:::-;7540:3;7561:67;7625:2;7620:3;7561:67;:::i;:::-;7554:74;;7637:93;7726:3;7637:93;:::i;:::-;7755:2;7750:3;7746:12;7739:19;;7398:366;;;:::o;7770:118::-;7857:24;7875:5;7857:24;:::i;:::-;7852:3;7845:37;7770:118;;:::o;7894:157::-;7999:45;8019:24;8037:5;8019:24;:::i;:::-;7999:45;:::i;:::-;7994:3;7987:58;7894:157;;:::o;8057:397::-;8197:3;8212:75;8283:3;8274:6;8212:75;:::i;:::-;8312:2;8307:3;8303:12;8296:19;;8325:75;8396:3;8387:6;8325:75;:::i;:::-;8425:2;8420:3;8416:12;8409:19;;8445:3;8438:10;;8057:397;;;;;:::o;8460:538::-;8628:3;8643:75;8714:3;8705:6;8643:75;:::i;:::-;8743:2;8738:3;8734:12;8727:19;;8756:75;8827:3;8818:6;8756:75;:::i;:::-;8856:2;8851:3;8847:12;8840:19;;8869:75;8940:3;8931:6;8869:75;:::i;:::-;8969:2;8964:3;8960:12;8953:19;;8989:3;8982:10;;8460:538;;;;;;:::o;9004:222::-;9097:4;9135:2;9124:9;9120:18;9112:26;;9148:71;9216:1;9205:9;9201:17;9192:6;9148:71;:::i;:::-;9004:222;;;;:::o;9232:332::-;9353:4;9391:2;9380:9;9376:18;9368:26;;9404:71;9472:1;9461:9;9457:17;9448:6;9404:71;:::i;:::-;9485:72;9553:2;9542:9;9538:18;9529:6;9485:72;:::i;:::-;9232:332;;;;;:::o;9570:::-;9691:4;9729:2;9718:9;9714:18;9706:26;;9742:71;9810:1;9799:9;9795:17;9786:6;9742:71;:::i;:::-;9823:72;9891:2;9880:9;9876:18;9867:6;9823:72;:::i;:::-;9570:332;;;;;:::o;9908:472::-;10072:4;10110:2;10099:9;10095:18;10087:26;;10123:71;10191:1;10180:9;10176:17;10167:6;10123:71;:::i;:::-;10204:72;10272:2;10261:9;10257:18;10248:6;10204:72;:::i;:::-;10286:87;10369:2;10358:9;10354:18;10345:6;10286:87;:::i;:::-;9908:472;;;;;;:::o;10386:210::-;10473:4;10511:2;10500:9;10496:18;10488:26;;10524:65;10586:1;10575:9;10571:17;10562:6;10524:65;:::i;:::-;10386:210;;;;:::o;10602:222::-;10695:4;10733:2;10722:9;10718:18;10710:26;;10746:71;10814:1;10803:9;10799:17;10790:6;10746:71;:::i;:::-;10602:222;;;;:::o;10830:419::-;10996:4;11034:2;11023:9;11019:18;11011:26;;11083:9;11077:4;11073:20;11069:1;11058:9;11054:17;11047:47;11111:131;11237:4;11111:131;:::i;:::-;11103:139;;10830:419;;;:::o;11255:::-;11421:4;11459:2;11448:9;11444:18;11436:26;;11508:9;11502:4;11498:20;11494:1;11483:9;11479:17;11472:47;11536:131;11662:4;11536:131;:::i;:::-;11528:139;;11255:419;;;:::o;11680:::-;11846:4;11884:2;11873:9;11869:18;11861:26;;11933:9;11927:4;11923:20;11919:1;11908:9;11904:17;11897:47;11961:131;12087:4;11961:131;:::i;:::-;11953:139;;11680:419;;;:::o;12105:::-;12271:4;12309:2;12298:9;12294:18;12286:26;;12358:9;12352:4;12348:20;12344:1;12333:9;12329:17;12322:47;12386:131;12512:4;12386:131;:::i;:::-;12378:139;;12105:419;;;:::o;12530:222::-;12623:4;12661:2;12650:9;12646:18;12638:26;;12674:71;12742:1;12731:9;12727:17;12718:6;12674:71;:::i;:::-;12530:222;;;;:::o;12758:553::-;12935:4;12973:3;12962:9;12958:19;12950:27;;12987:71;13055:1;13044:9;13040:17;13031:6;12987:71;:::i;:::-;13068:72;13136:2;13125:9;13121:18;13112:6;13068:72;:::i;:::-;13150;13218:2;13207:9;13203:18;13194:6;13150:72;:::i;:::-;13232;13300:2;13289:9;13285:18;13276:6;13232:72;:::i;:::-;12758:553;;;;;;;:::o;13317:740::-;13410:4;13416:6;13472:11;13459:25;13572:1;13566:4;13562:12;13551:8;13535:14;13531:29;13527:48;13507:18;13503:73;13493:168;;13580:79;;:::i;:::-;13493:168;13692:18;13682:8;13678:33;13670:41;;13744:4;13731:18;13721:28;;13772:18;13764:6;13761:30;13758:117;;;13794:79;;:::i;:::-;13758:117;13902:2;13896:4;13892:13;13884:21;;13959:4;13951:6;13947:17;13931:14;13927:38;13921:4;13917:49;13914:136;;;13969:79;;:::i;:::-;13914:136;13423:634;13317:740;;;;;:::o;14063:392::-;14155:4;14209:11;14196:25;14309:1;14303:4;14299:12;14288:8;14272:14;14268:29;14264:48;14244:18;14240:73;14230:168;;14317:79;;:::i;:::-;14230:168;14429:18;14419:8;14415:33;14407:41;;14160:295;14063:392;;;;:::o;14542:169::-;14626:11;14660:6;14655:3;14648:19;14700:4;14695:3;14691:14;14676:29;;14542:169;;;;:::o;14717:185::-;14757:1;14774:20;14792:1;14774:20;:::i;:::-;14769:25;;14808:20;14826:1;14808:20;:::i;:::-;14803:25;;14847:1;14837:35;;14852:18;;:::i;:::-;14837:35;14894:1;14891;14887:9;14882:14;;14717:185;;;;:::o;14908:96::-;14945:7;14974:24;14992:5;14974:24;:::i;:::-;14963:35;;14908:96;;;:::o;15010:90::-;15044:7;15087:5;15080:13;15073:21;15062:32;;15010:90;;;:::o;15106:77::-;15143:7;15172:5;15161:16;;15106:77;;;:::o;15189:126::-;15226:7;15266:42;15259:5;15255:54;15244:65;;15189:126;;;:::o;15321:77::-;15358:7;15387:5;15376:16;;15321:77;;;:::o;15404:128::-;15469:9;15502:24;15520:5;15502:24;:::i;:::-;15489:37;;15404:128;;;:::o;15538:233::-;15577:3;15600:24;15618:5;15600:24;:::i;:::-;15591:33;;15646:66;15639:5;15636:77;15633:103;;;15716:18;;:::i;:::-;15633:103;15763:1;15756:5;15752:13;15745:20;;15538:233;;;:::o;15777:100::-;15816:7;15845:26;15865:5;15845:26;:::i;:::-;15834:37;;15777:100;;;:::o;15883:79::-;15922:7;15951:5;15940:16;;15883:79;;;:::o;15968:94::-;16007:7;16036:20;16050:5;16036:20;:::i;:::-;16025:31;;15968:94;;;:::o;16068:79::-;16107:7;16136:5;16125:16;;16068:79;;;:::o;16153:176::-;16185:1;16202:20;16220:1;16202:20;:::i;:::-;16197:25;;16236:20;16254:1;16236:20;:::i;:::-;16231:25;;16275:1;16265:35;;16280:18;;:::i;:::-;16265:35;16321:1;16318;16314:9;16309:14;;16153:176;;;;:::o;16335:180::-;16383:77;16380:1;16373:88;16480:4;16477:1;16470:15;16504:4;16501:1;16494:15;16521:180;16569:77;16566:1;16559:88;16666:4;16663:1;16656:15;16690:4;16687:1;16680:15;16707:180;16755:77;16752:1;16745:88;16852:4;16849:1;16842:15;16876:4;16873:1;16866:15;16893:117;17002:1;16999;16992:12;17016:117;17125:1;17122;17115:12;17139:117;17248:1;17245;17238:12;17262:117;17371:1;17368;17361:12;17385:117;17494:1;17491;17484:12;17508:117;17617:1;17614;17607:12;17631:117;17740:1;17737;17730:12;17754:117;17863:1;17860;17853:12;17877:94;17910:8;17958:5;17954:2;17950:14;17929:35;;17877:94;;;:::o;17977:240::-;18117:34;18113:1;18105:6;18101:14;18094:58;18186:23;18181:2;18173:6;18169:15;18162:48;17977:240;:::o;18223:235::-;18363:34;18359:1;18351:6;18347:14;18340:58;18432:18;18427:2;18419:6;18415:15;18408:43;18223:235;:::o;18464:234::-;18604:34;18600:1;18592:6;18588:14;18581:58;18673:17;18668:2;18660:6;18656:15;18649:42;18464:234;:::o;18704:228::-;18844:34;18840:1;18832:6;18828:14;18821:58;18913:11;18908:2;18900:6;18896:15;18889:36;18704:228;:::o;18938:122::-;19011:24;19029:5;19011:24;:::i;:::-;19004:5;19001:35;18991:63;;19050:1;19047;19040:12;18991:63;18938:122;:::o;19066:116::-;19136:21;19151:5;19136:21;:::i;:::-;19129:5;19126:32;19116:60;;19172:1;19169;19162:12;19116:60;19066:116;:::o;19188:122::-;19261:24;19279:5;19261:24;:::i;:::-;19254:5;19251:35;19241:63;;19300:1;19297;19290:12;19241:63;19188:122;:::o;19316:::-;19389:24;19407:5;19389:24;:::i;:::-;19382:5;19379:35;19369:63;;19428:1;19425;19418:12;19369:63;19316:122;:::o

Swarm Source

ipfs://985af8a396f664e37df99f30185f939cb6de09ef6eea25dc5635c1e260630b7e
Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading