ETH Price: $2,505.44 (+0.15%)
Gas: 3.25 Gwei

Contract

0x374226dbAa3e44bF3923AfB63f5Fd83928B7e148
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw178810042023-08-10 0:45:47450 days ago1691628347IN
88mph : 88mph cUSDC Pool
0 ETH0.0006013418.87515609
Withdraw178810012023-08-10 0:45:11450 days ago1691628311IN
88mph : 88mph cUSDC Pool
0 ETH0.0060464718.47177553
Withdraw153489592022-08-15 23:41:09809 days ago1660606869IN
88mph : 88mph cUSDC Pool
0 ETH0.0059153816.93806025
Withdraw151518412022-07-16 5:26:17840 days ago1657949177IN
88mph : 88mph cUSDC Pool
0 ETH0.004577889.11263094
Withdraw148363432022-05-24 14:40:54892 days ago1653403254IN
88mph : 88mph cUSDC Pool
0 ETH0.0141499730.22485579
Withdraw148304292022-05-23 15:45:11893 days ago1653320711IN
88mph : 88mph cUSDC Pool
0 ETH0.012199924.5196088
Withdraw148189892022-05-21 19:08:11895 days ago1653160091IN
88mph : 88mph cUSDC Pool
0 ETH0.0087891818.77400199
Withdraw147983092022-05-18 10:25:53898 days ago1652869553IN
88mph : 88mph cUSDC Pool
0 ETH0.0062122513.26959945
Withdraw147559792022-05-11 16:47:01905 days ago1652287621IN
88mph : 88mph cUSDC Pool
0 ETH0.0647657133.46681359
Withdraw146815432022-04-29 21:07:37917 days ago1651266457IN
88mph : 88mph cUSDC Pool
0 ETH0.025565549.21582809
Withdraw145743012022-04-13 1:41:39934 days ago1649814099IN
88mph : 88mph cUSDC Pool
0 ETH0.0188826740.33406942
Withdraw145742852022-04-13 1:37:45934 days ago1649813865IN
88mph : 88mph cUSDC Pool
0 ETH0.0183626236.55294567
Withdraw145510322022-04-09 10:40:08937 days ago1649500808IN
88mph : 88mph cUSDC Pool
0 ETH0.0115109221.64695792
Withdraw142855672022-02-27 2:34:08979 days ago1645929248IN
88mph : 88mph cUSDC Pool
0 ETH0.0119141425.44902915
Withdraw140689542022-01-24 14:53:351012 days ago1643036015IN
88mph : 88mph cUSDC Pool
0 ETH0.0773751167.71199085
Withdraw140272962022-01-18 4:06:111019 days ago1642478771IN
88mph : 88mph cUSDC Pool
0 ETH0.0402036274.92889406
Withdraw139256012022-01-02 11:00:311034 days ago1641121231IN
88mph : 88mph cUSDC Pool
0 ETH0.02857458.88305827
Withdraw138680152021-12-24 12:56:491043 days ago1640350609IN
88mph : 88mph cUSDC Pool
0 ETH0.0195247441.70465567
Withdraw137954462021-12-13 7:24:131055 days ago1639380253IN
88mph : 88mph cUSDC Pool
0 ETH0.0202446941.71866582
Withdraw137952532021-12-13 6:42:251055 days ago1639377745IN
88mph : 88mph cUSDC Pool
0 ETH0.0295214360.83544824
Withdraw137823532021-12-11 6:55:151057 days ago1639205715IN
88mph : 88mph cUSDC Pool
0 ETH0.0286073956.94520822
Withdraw137551692021-12-06 23:31:331061 days ago1638833493IN
88mph : 88mph cUSDC Pool
0 ETH0.05050837107.88537306
Withdraw137333662021-12-03 11:29:591064 days ago1638530999IN
88mph : 88mph cUSDC Pool
0 ETH0.0360622274.31418737
Withdraw137170012021-11-30 20:49:321067 days ago1638305372IN
88mph : 88mph cUSDC Pool
0 ETH0.07962667164.08838224
Withdraw137023132021-11-28 12:12:071069 days ago1638101527IN
88mph : 88mph cUSDC Pool
0 ETH0.032436269.28341319
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xb1ABAac3...98e6473F2
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
DInterest

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 64 : DInterest.sol
pragma solidity 0.5.17;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "./libs/DecMath.sol";
import "./moneymarkets/IMoneyMarket.sol";
import "./models/fee/IFeeModel.sol";
import "./models/interest/IInterestModel.sol";
import "./NFT.sol";
import "./rewards/MPHMinter.sol";
import "./models/interest-oracle/IInterestOracle.sol";

// DeLorean Interest -- It's coming back from the future!
// EL PSY CONGROO
// Author: Zefram Lou
// Contact: [email protected]
contract DInterest is ReentrancyGuard, Ownable {
    using SafeMath for uint256;
    using DecMath for uint256;
    using SafeERC20 for ERC20;
    using Address for address;

    // Constants
    uint256 internal constant PRECISION = 10**18;
    uint256 internal constant ONE = 10**18;

    // User deposit data
    // Each deposit has an ID used in the depositNFT, which is equal to its index in `deposits` plus 1
    struct Deposit {
        uint256 amount; // Amount of stablecoin deposited
        uint256 maturationTimestamp; // Unix timestamp after which the deposit may be withdrawn, in seconds
        uint256 interestOwed; // Deficit incurred to the pool at time of deposit
        uint256 initialMoneyMarketIncomeIndex; // Money market's income index at time of deposit
        bool active; // True if not yet withdrawn, false if withdrawn
        bool finalSurplusIsNegative;
        uint256 finalSurplusAmount; // Surplus remaining after withdrawal
        uint256 mintMPHAmount; // Amount of MPH minted to user
        uint256 depositTimestamp; // Unix timestamp at time of deposit, in seconds
    }
    Deposit[] internal deposits;
    uint256 public latestFundedDepositID; // the ID of the most recently created deposit that was funded
    uint256 public unfundedUserDepositAmount; // the deposited stablecoin amount whose deficit hasn't been funded

    // Funding data
    // Each funding has an ID used in the fundingNFT, which is equal to its index in `fundingList` plus 1
    struct Funding {
        // deposits with fromDepositID < ID <= toDepositID are funded
        uint256 fromDepositID;
        uint256 toDepositID;
        uint256 recordedFundedDepositAmount;
        uint256 recordedMoneyMarketIncomeIndex;
        uint256 creationTimestamp; // Unix timestamp at time of deposit, in seconds
    }
    Funding[] internal fundingList;

    // Params
    uint256 public MinDepositPeriod; // Minimum deposit period, in seconds
    uint256 public MaxDepositPeriod; // Maximum deposit period, in seconds
    uint256 public MinDepositAmount; // Minimum deposit amount for each deposit, in stablecoins
    uint256 public MaxDepositAmount; // Maximum deposit amount for each deposit, in stablecoins

    // Instance variables
    uint256 public totalDeposit;
    uint256 public totalInterestOwed;

    // External smart contracts
    IMoneyMarket public moneyMarket;
    ERC20 public stablecoin;
    IFeeModel public feeModel;
    IInterestModel public interestModel;
    IInterestOracle public interestOracle;
    NFT public depositNFT;
    NFT public fundingNFT;
    MPHMinter public mphMinter;

    // Events
    event EDeposit(
        address indexed sender,
        uint256 indexed depositID,
        uint256 amount,
        uint256 maturationTimestamp,
        uint256 interestAmount,
        uint256 mintMPHAmount
    );
    event EWithdraw(
        address indexed sender,
        uint256 indexed depositID,
        uint256 indexed fundingID,
        bool early,
        uint256 takeBackMPHAmount
    );
    event EFund(
        address indexed sender,
        uint256 indexed fundingID,
        uint256 deficitAmount
    );
    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
    event ESetParamUint(
        address indexed sender,
        string indexed paramName,
        uint256 newValue
    );

    struct DepositLimit {
        uint256 MinDepositPeriod;
        uint256 MaxDepositPeriod;
        uint256 MinDepositAmount;
        uint256 MaxDepositAmount;
    }

    constructor(
        DepositLimit memory _depositLimit,
        address _moneyMarket, // Address of IMoneyMarket that's used for generating interest (owner must be set to this DInterest contract)
        address _stablecoin, // Address of the stablecoin used to store funds
        address _feeModel, // Address of the FeeModel contract that determines how fees are charged
        address _interestModel, // Address of the InterestModel contract that determines how much interest to offer
        address _interestOracle, // Address of the InterestOracle contract that provides the average interest rate
        address _depositNFT, // Address of the NFT representing ownership of deposits (owner must be set to this DInterest contract)
        address _fundingNFT, // Address of the NFT representing ownership of fundings (owner must be set to this DInterest contract)
        address _mphMinter // Address of the contract for handling minting MPH to users
    ) public {
        // Verify input addresses
        require(
            _moneyMarket.isContract() &&
                _stablecoin.isContract() &&
                _feeModel.isContract() &&
                _interestModel.isContract() &&
                _interestOracle.isContract() &&
                _depositNFT.isContract() &&
                _fundingNFT.isContract() &&
                _mphMinter.isContract(),
            "DInterest: An input address is not a contract"
        );

        moneyMarket = IMoneyMarket(_moneyMarket);
        stablecoin = ERC20(_stablecoin);
        feeModel = IFeeModel(_feeModel);
        interestModel = IInterestModel(_interestModel);
        interestOracle = IInterestOracle(_interestOracle);
        depositNFT = NFT(_depositNFT);
        fundingNFT = NFT(_fundingNFT);
        mphMinter = MPHMinter(_mphMinter);

        // Ensure moneyMarket uses the same stablecoin
        require(
            moneyMarket.stablecoin() == _stablecoin,
            "DInterest: moneyMarket.stablecoin() != _stablecoin"
        );

        // Ensure interestOracle uses the same moneyMarket
        require(
            interestOracle.moneyMarket() == _moneyMarket,
            "DInterest: interestOracle.moneyMarket() != _moneyMarket"
        );

        // Verify input uint256 parameters
        require(
            _depositLimit.MaxDepositPeriod > 0 &&
                _depositLimit.MaxDepositAmount > 0,
            "DInterest: An input uint256 is 0"
        );
        require(
            _depositLimit.MinDepositPeriod <= _depositLimit.MaxDepositPeriod,
            "DInterest: Invalid DepositPeriod range"
        );
        require(
            _depositLimit.MinDepositAmount <= _depositLimit.MaxDepositAmount,
            "DInterest: Invalid DepositAmount range"
        );

        MinDepositPeriod = _depositLimit.MinDepositPeriod;
        MaxDepositPeriod = _depositLimit.MaxDepositPeriod;
        MinDepositAmount = _depositLimit.MinDepositAmount;
        MaxDepositAmount = _depositLimit.MaxDepositAmount;
        totalDeposit = 0;
    }

    /**
        Public actions
     */

    function deposit(uint256 amount, uint256 maturationTimestamp)
        external
        nonReentrant
    {
        _deposit(amount, maturationTimestamp);
    }

    function withdraw(uint256 depositID, uint256 fundingID)
        external
        nonReentrant
    {
        _withdraw(depositID, fundingID, false);
    }

    function earlyWithdraw(uint256 depositID, uint256 fundingID)
        external
        nonReentrant
    {
        _withdraw(depositID, fundingID, true);
    }

    function multiDeposit(
        uint256[] calldata amountList,
        uint256[] calldata maturationTimestampList
    ) external nonReentrant {
        require(
            amountList.length == maturationTimestampList.length,
            "DInterest: List lengths unequal"
        );
        for (uint256 i = 0; i < amountList.length; i = i.add(1)) {
            _deposit(amountList[i], maturationTimestampList[i]);
        }
    }

    function multiWithdraw(
        uint256[] calldata depositIDList,
        uint256[] calldata fundingIDList
    ) external nonReentrant {
        require(
            depositIDList.length == fundingIDList.length,
            "DInterest: List lengths unequal"
        );
        for (uint256 i = 0; i < depositIDList.length; i = i.add(1)) {
            _withdraw(depositIDList[i], fundingIDList[i], false);
        }
    }

    function multiEarlyWithdraw(
        uint256[] calldata depositIDList,
        uint256[] calldata fundingIDList
    ) external nonReentrant {
        require(
            depositIDList.length == fundingIDList.length,
            "DInterest: List lengths unequal"
        );
        for (uint256 i = 0; i < depositIDList.length; i = i.add(1)) {
            _withdraw(depositIDList[i], fundingIDList[i], true);
        }
    }

    /**
        Deficit funding
     */

    function fundAll() external nonReentrant {
        // Calculate current deficit
        (bool isNegative, uint256 deficit) = surplus();
        require(isNegative, "DInterest: No deficit available");
        require(
            !depositIsFunded(deposits.length),
            "DInterest: All deposits funded"
        );

        // Create funding struct
        uint256 incomeIndex = moneyMarket.incomeIndex();
        require(incomeIndex > 0, "DInterest: incomeIndex == 0");
        fundingList.push(
            Funding({
                fromDepositID: latestFundedDepositID,
                toDepositID: deposits.length,
                recordedFundedDepositAmount: unfundedUserDepositAmount,
                recordedMoneyMarketIncomeIndex: incomeIndex,
                creationTimestamp: now
            })
        );

        // Update relevant values
        latestFundedDepositID = deposits.length;
        unfundedUserDepositAmount = 0;

        _fund(deficit);
    }

    function fundMultiple(uint256 toDepositID) external nonReentrant {
        require(
            toDepositID > latestFundedDepositID,
            "DInterest: Deposits already funded"
        );
        require(
            toDepositID <= deposits.length,
            "DInterest: Invalid toDepositID"
        );

        (bool isNegative, uint256 surplus) = surplus();
        require(isNegative, "DInterest: No deficit available");

        uint256 totalDeficit = 0;
        uint256 totalSurplus = 0;
        uint256 totalDepositToFund = 0;
        // Deposits with ID [latestFundedDepositID+1, toDepositID] will be funded
        for (
            uint256 id = latestFundedDepositID.add(1);
            id <= toDepositID;
            id = id.add(1)
        ) {
            Deposit storage depositEntry = _getDeposit(id);
            if (depositEntry.active) {
                // Deposit still active, use current surplus
                (isNegative, surplus) = surplusOfDeposit(id);
            } else {
                // Deposit has been withdrawn, use recorded final surplus
                (isNegative, surplus) = (
                    depositEntry.finalSurplusIsNegative,
                    depositEntry.finalSurplusAmount
                );
            }

            if (isNegative) {
                // Add on deficit to total
                totalDeficit = totalDeficit.add(surplus);
            } else {
                // Has surplus
                totalSurplus = totalSurplus.add(surplus);
            }

            if (depositEntry.active) {
                totalDepositToFund = totalDepositToFund.add(
                    depositEntry.amount
                );
            }
        }
        if (totalSurplus >= totalDeficit) {
            // Deposits selected have a surplus as a whole, revert
            revert("DInterest: Selected deposits in surplus");
        } else {
            // Deduct surplus from totalDeficit
            totalDeficit = totalDeficit.sub(totalSurplus);
        }

        // Create funding struct
        uint256 incomeIndex = moneyMarket.incomeIndex();
        require(incomeIndex > 0, "DInterest: incomeIndex == 0");
        fundingList.push(
            Funding({
                fromDepositID: latestFundedDepositID,
                toDepositID: toDepositID,
                recordedFundedDepositAmount: totalDepositToFund,
                recordedMoneyMarketIncomeIndex: incomeIndex,
                creationTimestamp: now
            })
        );

        // Update relevant values
        latestFundedDepositID = toDepositID;
        unfundedUserDepositAmount = unfundedUserDepositAmount.sub(
            totalDepositToFund
        );

        _fund(totalDeficit);
    }

    /**
        Public getters
     */

    function calculateInterestAmount(
        uint256 depositAmount,
        uint256 depositPeriodInSeconds
    ) public returns (uint256 interestAmount) {
        (, uint256 moneyMarketInterestRatePerSecond) = interestOracle
            .updateAndQuery();
        (bool surplusIsNegative, uint256 surplusAmount) = surplus();

        return
            interestModel.calculateInterestAmount(
                depositAmount,
                depositPeriodInSeconds,
                moneyMarketInterestRatePerSecond,
                surplusIsNegative,
                surplusAmount
            );
    }

    function surplus() public returns (bool isNegative, uint256 surplusAmount) {
        uint256 totalValue = moneyMarket.totalValue();
        uint256 totalOwed = totalDeposit.add(totalInterestOwed);
        if (totalValue >= totalOwed) {
            // Locked value more than owed deposits, positive surplus
            isNegative = false;
            surplusAmount = totalValue.sub(totalOwed);
        } else {
            // Locked value less than owed deposits, negative surplus
            isNegative = true;
            surplusAmount = totalOwed.sub(totalValue);
        }
    }

    function surplusOfDeposit(uint256 depositID)
        public
        returns (bool isNegative, uint256 surplusAmount)
    {
        Deposit storage depositEntry = _getDeposit(depositID);
        uint256 currentMoneyMarketIncomeIndex = moneyMarket.incomeIndex();
        uint256 currentDepositValue = depositEntry
            .amount
            .mul(currentMoneyMarketIncomeIndex)
            .div(depositEntry.initialMoneyMarketIncomeIndex);
        uint256 owed = depositEntry.amount.add(depositEntry.interestOwed);
        if (currentDepositValue >= owed) {
            // Locked value more than owed deposits, positive surplus
            isNegative = false;
            surplusAmount = currentDepositValue.sub(owed);
        } else {
            // Locked value less than owed deposits, negative surplus
            isNegative = true;
            surplusAmount = owed.sub(currentDepositValue);
        }
    }

    function depositIsFunded(uint256 id) public view returns (bool) {
        return (id <= latestFundedDepositID);
    }

    function depositsLength() external view returns (uint256) {
        return deposits.length;
    }

    function fundingListLength() external view returns (uint256) {
        return fundingList.length;
    }

    function getDeposit(uint256 depositID)
        external
        view
        returns (Deposit memory)
    {
        return deposits[depositID.sub(1)];
    }

    function getFunding(uint256 fundingID)
        external
        view
        returns (Funding memory)
    {
        return fundingList[fundingID.sub(1)];
    }

    function moneyMarketIncomeIndex() external returns (uint256) {
        return moneyMarket.incomeIndex();
    }

    /**
        Param setters
     */
    function setFeeModel(address newValue) external onlyOwner {
        require(newValue.isContract(), "DInterest: not contract");
        feeModel = IFeeModel(newValue);
        emit ESetParamAddress(msg.sender, "feeModel", newValue);
    }

    function setInterestModel(address newValue) external onlyOwner {
        require(newValue.isContract(), "DInterest: not contract");
        interestModel = IInterestModel(newValue);
        emit ESetParamAddress(msg.sender, "interestModel", newValue);
    }

    function setInterestOracle(address newValue) external onlyOwner {
        require(newValue.isContract(), "DInterest: not contract");
        interestOracle = IInterestOracle(newValue);
        emit ESetParamAddress(msg.sender, "interestOracle", newValue);
    }

    function setRewards(address newValue) external onlyOwner {
        require(newValue.isContract(), "DInterest: not contract");
        moneyMarket.setRewards(newValue);
        emit ESetParamAddress(msg.sender, "moneyMarket.rewards", newValue);
    }

    function setMPHMinter(address newValue) external onlyOwner {
        require(newValue.isContract(), "DInterest: not contract");
        mphMinter = MPHMinter(newValue);
        emit ESetParamAddress(msg.sender, "mphMinter", newValue);
    }

    function setMinDepositPeriod(uint256 newValue) external onlyOwner {
        require(newValue <= MaxDepositPeriod, "DInterest: invalid value");
        MinDepositPeriod = newValue;
        emit ESetParamUint(msg.sender, "MinDepositPeriod", newValue);
    }

    function setMaxDepositPeriod(uint256 newValue) external onlyOwner {
        require(
            newValue >= MinDepositPeriod && newValue > 0,
            "DInterest: invalid value"
        );
        MaxDepositPeriod = newValue;
        emit ESetParamUint(msg.sender, "MaxDepositPeriod", newValue);
    }

    function setMinDepositAmount(uint256 newValue) external onlyOwner {
        require(newValue <= MaxDepositAmount, "DInterest: invalid value");
        MinDepositAmount = newValue;
        emit ESetParamUint(msg.sender, "MinDepositAmount", newValue);
    }

    function setMaxDepositAmount(uint256 newValue) external onlyOwner {
        require(
            newValue >= MinDepositAmount && newValue > 0,
            "DInterest: invalid value"
        );
        MaxDepositAmount = newValue;
        emit ESetParamUint(msg.sender, "MaxDepositAmount", newValue);
    }

    function setDepositNFTTokenURI(uint256 tokenId, string calldata newURI)
        external
        onlyOwner
    {
        depositNFT.setTokenURI(tokenId, newURI);
    }

    function setDepositNFTBaseURI(string calldata newURI) external onlyOwner {
        depositNFT.setBaseURI(newURI);
    }

    function setDepositNFTContractURI(string calldata newURI)
        external
        onlyOwner
    {
        depositNFT.setContractURI(newURI);
    }

    function setFundingNFTTokenURI(uint256 tokenId, string calldata newURI)
        external
        onlyOwner
    {
        fundingNFT.setTokenURI(tokenId, newURI);
    }

    function setFundingNFTBaseURI(string calldata newURI) external onlyOwner {
        fundingNFT.setBaseURI(newURI);
    }

    function setFundingNFTContractURI(string calldata newURI)
        external
        onlyOwner
    {
        fundingNFT.setContractURI(newURI);
    }

    /**
        Internal getters
     */

    function _getDeposit(uint256 depositID)
        internal
        view
        returns (Deposit storage)
    {
        return deposits[depositID.sub(1)];
    }

    function _getFunding(uint256 fundingID)
        internal
        view
        returns (Funding storage)
    {
        return fundingList[fundingID.sub(1)];
    }

    /**
        Internals
     */

    function _deposit(uint256 amount, uint256 maturationTimestamp) internal {
        // Cannot deposit 0
        require(amount > 0, "DInterest: Deposit amount is 0");

        // Ensure deposit amount is not more than maximum
        require(
            amount >= MinDepositAmount && amount <= MaxDepositAmount,
            "DInterest: Deposit amount out of range"
        );

        // Ensure deposit period is at least MinDepositPeriod
        uint256 depositPeriod = maturationTimestamp.sub(now);
        require(
            depositPeriod >= MinDepositPeriod &&
                depositPeriod <= MaxDepositPeriod,
            "DInterest: Deposit period out of range"
        );

        // Update totalDeposit
        totalDeposit = totalDeposit.add(amount);

        // Update funding related data
        uint256 id = deposits.length.add(1);
        unfundedUserDepositAmount = unfundedUserDepositAmount.add(amount);

        // Calculate interest
        uint256 interestAmount = calculateInterestAmount(amount, depositPeriod);
        require(interestAmount > 0, "DInterest: interestAmount == 0");

        // Update totalInterestOwed
        totalInterestOwed = totalInterestOwed.add(interestAmount);

        // Mint MPH for msg.sender
        uint256 mintMPHAmount = mphMinter.mintDepositorReward(
            msg.sender,
            amount,
            depositPeriod,
            interestAmount
        );

        // Record deposit data for `msg.sender`
        deposits.push(
            Deposit({
                amount: amount,
                maturationTimestamp: maturationTimestamp,
                interestOwed: interestAmount,
                initialMoneyMarketIncomeIndex: moneyMarket.incomeIndex(),
                active: true,
                finalSurplusIsNegative: false,
                finalSurplusAmount: 0,
                mintMPHAmount: mintMPHAmount,
                depositTimestamp: now
            })
        );

        // Transfer `amount` stablecoin to DInterest
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Lend `amount` stablecoin to money market
        stablecoin.safeIncreaseAllowance(address(moneyMarket), amount);
        moneyMarket.deposit(amount);

        // Mint depositNFT
        depositNFT.mint(msg.sender, id);

        // Emit event
        emit EDeposit(
            msg.sender,
            id,
            amount,
            maturationTimestamp,
            interestAmount,
            mintMPHAmount
        );
    }

    function _withdraw(
        uint256 depositID,
        uint256 fundingID,
        bool early
    ) internal {
        Deposit storage depositEntry = _getDeposit(depositID);

        // Verify deposit is active and set to inactive
        require(depositEntry.active, "DInterest: Deposit not active");
        depositEntry.active = false;

        if (early) {
            // Verify `now < depositEntry.maturationTimestamp`
            require(
                now < depositEntry.maturationTimestamp,
                "DInterest: Deposit mature, use withdraw() instead"
            );
            // Verify `now > depositEntry.depositTimestamp`
            require(
                now > depositEntry.depositTimestamp,
                "DInterest: Deposited in same block"
            );
        } else {
            // Verify `now >= depositEntry.maturationTimestamp`
            require(
                now >= depositEntry.maturationTimestamp,
                "DInterest: Deposit not mature"
            );
        }

        // Verify msg.sender owns the depositNFT
        require(
            depositNFT.ownerOf(depositID) == msg.sender,
            "DInterest: Sender doesn't own depositNFT"
        );

        // Take back MPH
        uint256 takeBackMPHAmount = mphMinter.takeBackDepositorReward(
            msg.sender,
            depositEntry.mintMPHAmount,
            early
        );

        // Update totalDeposit
        totalDeposit = totalDeposit.sub(depositEntry.amount);

        // Update totalInterestOwed
        totalInterestOwed = totalInterestOwed.sub(depositEntry.interestOwed);

        uint256 feeAmount;
        uint256 withdrawAmount;
        if (early) {
            // Withdraw the principal of the deposit from money market
            withdrawAmount = depositEntry.amount;
        } else {
            // Withdraw the principal & the interest from money market
            feeAmount = feeModel.getFee(depositEntry.interestOwed);
            withdrawAmount = depositEntry.amount.add(depositEntry.interestOwed);
        }
        withdrawAmount = moneyMarket.withdraw(withdrawAmount);

        (
            bool depositSurplusIsNegative,
            uint256 depositSurplus
        ) = surplusOfDeposit(depositID);

        // If deposit was funded, payout interest to funder
        if (depositIsFunded(depositID)) {
            _payInterestToFunder(
                fundingID,
                depositID,
                depositEntry.amount,
                depositEntry.maturationTimestamp,
                depositSurplusIsNegative,
                depositSurplus,
                early
            );
        } else {
            // Remove deposit from future deficit fundings
            unfundedUserDepositAmount = unfundedUserDepositAmount.sub(
                depositEntry.amount
            );

            // Record remaining surplus
            depositEntry.finalSurplusIsNegative = depositSurplusIsNegative;
            depositEntry.finalSurplusAmount = depositSurplus;
        }

        // Send `withdrawAmount - feeAmount` stablecoin to `msg.sender`
        stablecoin.safeTransfer(msg.sender, withdrawAmount.sub(feeAmount));

        // Send `feeAmount` stablecoin to feeModel beneficiary
        stablecoin.safeTransfer(feeModel.beneficiary(), feeAmount);

        // Emit event
        emit EWithdraw(
            msg.sender,
            depositID,
            fundingID,
            early,
            takeBackMPHAmount
        );
    }

    function _payInterestToFunder(
        uint256 fundingID,
        uint256 depositID,
        uint256 depositAmount,
        uint256 depositMaturationTimestamp,
        bool depositSurplusIsNegative,
        uint256 depositSurplus,
        bool early
    ) internal {
        Funding storage f = _getFunding(fundingID);
        require(
            depositID > f.fromDepositID && depositID <= f.toDepositID,
            "DInterest: Deposit not funded by fundingID"
        );
        uint256 currentMoneyMarketIncomeIndex = moneyMarket.incomeIndex();
        require(
            currentMoneyMarketIncomeIndex > 0,
            "DInterest: currentMoneyMarketIncomeIndex == 0"
        );
        uint256 interestAmount = f
            .recordedFundedDepositAmount
            .mul(currentMoneyMarketIncomeIndex)
            .div(f.recordedMoneyMarketIncomeIndex)
            .sub(f.recordedFundedDepositAmount);

        // Update funding values
        f.recordedFundedDepositAmount = f.recordedFundedDepositAmount.sub(
            depositAmount
        );
        f.recordedMoneyMarketIncomeIndex = currentMoneyMarketIncomeIndex;

        // Send interest to funder
        address funder = fundingNFT.ownerOf(fundingID);
        uint256 transferToFunderAmount = (early && depositSurplusIsNegative)
            ? interestAmount.add(depositSurplus)
            : interestAmount;
        if (transferToFunderAmount > 0) {
            transferToFunderAmount = moneyMarket.withdraw(
                transferToFunderAmount
            );
            stablecoin.safeTransfer(funder, transferToFunderAmount);
        }

        // Mint funder rewards
        mphMinter.mintFunderReward(
            funder,
            depositAmount,
            f.creationTimestamp,
            depositMaturationTimestamp,
            interestAmount,
            early
        );
    }

    function _fund(uint256 totalDeficit) internal {
        // Transfer `totalDeficit` stablecoins from msg.sender
        stablecoin.safeTransferFrom(msg.sender, address(this), totalDeficit);

        // Deposit `totalDeficit` stablecoins into moneyMarket
        stablecoin.safeIncreaseAllowance(address(moneyMarket), totalDeficit);
        moneyMarket.deposit(totalDeficit);

        // Mint fundingNFT
        fundingNFT.mint(msg.sender, fundingList.length);

        // Emit event
        uint256 fundingID = fundingList.length;
        emit EFund(msg.sender, fundingID, totalDeficit);
    }
}

File 2 of 64 : SafeMath.sol
pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 3 of 64 : ERC20.sol
pragma solidity ^0.5.0;

import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.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 {ERC20Mintable}.
 *
 * 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 guidelines: functions revert instead
 * of 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 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view 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 returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public 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 returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        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 returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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 returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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 {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(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
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(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 {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is 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 {
        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 Destroys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See {_burn} and {_approve}.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
    }
}

File 4 of 64 : Context.sol
pragma solidity ^0.5.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 GSN 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.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 5 of 64 : IERC20.sol
pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
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 6 of 64 : SafeERC20.sol
pragma solidity ^0.5.0;

import "./IERC20.sol";
import "../../math/SafeMath.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 ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    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));
    }

    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'
        // solhint-disable-next-line max-line-length
        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).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        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.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 7 of 64 : Address.sol
pragma solidity ^0.5.5;

/**
 * @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) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }

    /**
     * @dev Converts an `address` into `address payable`. Note that this is
     * simply a type cast: the actual underlying value is not changed.
     *
     * _Available since v2.4.0._
     */
    function toPayable(address account) internal pure returns (address payable) {
        return address(uint160(account));
    }

    /**
     * @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].
     *
     * _Available since v2.4.0._
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-call-value
        (bool success, ) = recipient.call.value(amount)("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
}

File 8 of 64 : ReentrancyGuard.sol
pragma solidity ^0.5.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].
 *
 * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
 * metering changes introduced in the Istanbul hardfork.
 */
contract ReentrancyGuard {
    bool private _notEntered;

    constructor () internal {
        // Storing an initial 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 percetange 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.
        _notEntered = true;
    }

    /**
     * @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(_notEntered, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _notEntered = false;

        _;

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

File 9 of 64 : Ownable.sol
pragma solidity ^0.5.0;

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 10 of 64 : DecMath.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";

// Decimal math library
library DecMath {
    using SafeMath for uint256;

    uint256 internal constant PRECISION = 10**18;

    function decmul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mul(b).div(PRECISION);
    }

    function decdiv(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mul(PRECISION).div(b);
    }
}

File 11 of 64 : IMoneyMarket.sol
pragma solidity 0.5.17;

// Interface for money market protocols (Compound, Aave, bZx, etc.)
interface IMoneyMarket {
    function deposit(uint256 amount) external;

    function withdraw(uint256 amountInUnderlying)
        external
        returns (uint256 actualAmountWithdrawn);

    function claimRewards() external; // Claims farmed tokens (e.g. COMP, CRV) and sends it to the rewards pool

    function totalValue() external returns (uint256); // The total value locked in the money market, in terms of the underlying stablecoin

    function incomeIndex() external returns (uint256); // Used for calculating the interest generated (e.g. cDai's price for the Compound market)

    function stablecoin() external view returns (address);

    function setRewards(address newValue) external;

    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
}

File 12 of 64 : IFeeModel.sol
pragma solidity 0.5.17;

interface IFeeModel {
    function beneficiary() external view returns (address payable);

    function getFee(uint256 _txAmount)
        external
        pure
        returns (uint256 _feeAmount);
}

File 13 of 64 : IInterestModel.sol
pragma solidity 0.5.17;

interface IInterestModel {
    function calculateInterestAmount(
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 moneyMarketInterestRatePerSecond,
        bool surplusIsNegative,
        uint256 surplusAmount
    ) external view returns (uint256 interestAmount);
}

File 14 of 64 : NFT.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC721/ERC721Metadata.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";

contract NFT is ERC721Metadata, Ownable {
    string internal _contractURI;

    constructor(string memory name, string memory symbol)
        public
        ERC721Metadata(name, symbol)
    {}

    function contractURI() external view returns (string memory) {
        return _contractURI;
    }

    function mint(address to, uint256 tokenId) external onlyOwner {
        _safeMint(to, tokenId);
    }

    function burn(uint256 tokenId) external onlyOwner {
        _burn(tokenId);
    }

    function setContractURI(string calldata newURI) external onlyOwner {
        _contractURI = newURI;
    }

    function setTokenURI(uint256 tokenId, string calldata newURI)
        external
        onlyOwner
    {
        _setTokenURI(tokenId, newURI);
    }

    function setBaseURI(string calldata newURI) external onlyOwner {
        _setBaseURI(newURI);
    }
}

File 15 of 64 : ERC721Metadata.sol
pragma solidity ^0.5.0;

import "../../GSN/Context.sol";
import "./ERC721.sol";
import "./IERC721Metadata.sol";
import "../../introspection/ERC165.sol";

contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata {
    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Base URI
    string private _baseURI;

    // Optional mapping for token URIs
    mapping(uint256 => string) private _tokenURIs;

    /*
     *     bytes4(keccak256('name()')) == 0x06fdde03
     *     bytes4(keccak256('symbol()')) == 0x95d89b41
     *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
     *
     *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
     */
    bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;

    /**
     * @dev Constructor function
     */
    constructor (string memory name, string memory symbol) public {
        _name = name;
        _symbol = symbol;

        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721_METADATA);
    }

    /**
     * @dev Gets the token name.
     * @return string representing the token name
     */
    function name() external view returns (string memory) {
        return _name;
    }

    /**
     * @dev Gets the token symbol.
     * @return string representing the token symbol
     */
    function symbol() external view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the URI for a given token ID. May return an empty string.
     *
     * If the token's URI is non-empty and a base URI was set (via
     * {_setBaseURI}), it will be added to the token ID's URI as a prefix.
     *
     * Reverts if the token ID does not exist.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];

        // Even if there is a base URI, it is only appended to non-empty token-specific URIs
        if (bytes(_tokenURI).length == 0) {
            return "";
        } else {
            // abi.encodePacked is being used to concatenate strings
            return string(abi.encodePacked(_baseURI, _tokenURI));
        }
    }

    /**
     * @dev Internal function to set the token URI for a given token.
     *
     * Reverts if the token ID does not exist.
     *
     * TIP: if all token IDs share a prefix (e.g. if your URIs look like
     * `http://api.myproject.com/token/<id>`), use {_setBaseURI} to store
     * it and save gas.
     */
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal {
        require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }

    /**
     * @dev Internal function to set the base URI for all token IDs. It is
     * automatically added as a prefix to the value returned in {tokenURI}.
     *
     * _Available since v2.5.0._
     */
    function _setBaseURI(string memory baseURI) internal {
        _baseURI = baseURI;
    }

    /**
    * @dev Returns the base URI set via {_setBaseURI}. This will be
    * automatically added as a preffix in {tokenURI} to each token's URI, when
    * they are non-empty.
    *
    * _Available since v2.5.0._
    */
    function baseURI() external view returns (string memory) {
        return _baseURI;
    }

    /**
     * @dev Internal function to burn a specific token.
     * Reverts if the token does not exist.
     * Deprecated, use _burn(uint256) instead.
     * @param owner owner of the token to burn
     * @param tokenId uint256 ID of the token being burned by the msg.sender
     */
    function _burn(address owner, uint256 tokenId) internal {
        super._burn(owner, tokenId);

        // Clear metadata (if any)
        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }
    }
}

File 16 of 64 : ERC721.sol
pragma solidity ^0.5.0;

import "../../GSN/Context.sol";
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../drafts/Counters.sol";
import "../../introspection/ERC165.sol";

/**
 * @title ERC721 Non-Fungible Token Standard basic implementation
 * @dev see https://eips.ethereum.org/EIPS/eip-721
 */
contract ERC721 is Context, ERC165, IERC721 {
    using SafeMath for uint256;
    using Address for address;
    using Counters for Counters.Counter;

    // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    // Mapping from token ID to owner
    mapping (uint256 => address) private _tokenOwner;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to number of owned token
    mapping (address => Counters.Counter) private _ownedTokensCount;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    /*
     *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
     *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
     *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
     *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
     *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
     *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
     *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
     *
     *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
     *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
     */
    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;

    constructor () public {
        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721);
    }

    /**
     * @dev Gets the balance of the specified address.
     * @param owner address to query the balance of
     * @return uint256 representing the amount owned by the passed address
     */
    function balanceOf(address owner) public view returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");

        return _ownedTokensCount[owner].current();
    }

    /**
     * @dev Gets the owner of the specified token ID.
     * @param tokenId uint256 ID of the token to query the owner of
     * @return address currently marked as the owner of the given token ID
     */
    function ownerOf(uint256 tokenId) public view returns (address) {
        address owner = _tokenOwner[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");

        return owner;
    }

    /**
     * @dev Approves another address to transfer the given token ID
     * The zero address indicates there is no approved address.
     * There can only be one approved address per token at a given time.
     * Can only be called by the token owner or an approved operator.
     * @param to address to be approved for the given token ID
     * @param tokenId uint256 ID of the token to be approved
     */
    function approve(address to, uint256 tokenId) public {
        address owner = ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Gets the approved address for a token ID, or zero if no address set
     * Reverts if the token ID does not exist.
     * @param tokenId uint256 ID of the token to query the approval of
     * @return address currently approved for the given token ID
     */
    function getApproved(uint256 tokenId) public view returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Sets or unsets the approval of a given operator
     * An operator is allowed to transfer all tokens of the sender on their behalf.
     * @param to operator address to set the approval
     * @param approved representing the status of the approval to be set
     */
    function setApprovalForAll(address to, bool approved) public {
        require(to != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][to] = approved;
        emit ApprovalForAll(_msgSender(), to, approved);
    }

    /**
     * @dev Tells whether an operator is approved by a given owner.
     * @param owner owner address which you want to query the approval of
     * @param operator operator address which you want to query the approval of
     * @return bool whether the given operator is approved by the given owner
     */
    function isApprovedForAll(address owner, address operator) public view returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev Transfers the ownership of a given token ID to another address.
     * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     * Requires the msg.sender to be the owner, approved, or operator.
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function transferFrom(address from, address to, uint256 tokenId) public {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transferFrom(from, to, tokenId);
    }

    /**
     * @dev Safely transfers the ownership of a given token ID to another address
     * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * Requires the msg.sender to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev Safely transfers the ownership of a given token ID to another address
     * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * Requires the _msgSender() to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes data to send along with a safe transfer check
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransferFrom(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers the ownership of a given token ID to another address
     * If the target address is a contract, it must implement `onERC721Received`,
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * Requires the msg.sender to be the owner, approved, or operator
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes data to send along with a safe transfer check
     */
    function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal {
        _transferFrom(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether the specified token exists.
     * @param tokenId uint256 ID of the token to query the existence of
     * @return bool whether the token exists
     */
    function _exists(uint256 tokenId) internal view returns (bool) {
        address owner = _tokenOwner[tokenId];
        return owner != address(0);
    }

    /**
     * @dev Returns whether the given spender can transfer a given token ID.
     * @param spender address of the spender to query
     * @param tokenId uint256 ID of the token to be transferred
     * @return bool whether the msg.sender is approved for the given token ID,
     * is an operator of the owner, or is the owner of the token
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Internal function to safely mint a new token.
     * Reverts if the given token ID already exists.
     * If the target address is a contract, it must implement `onERC721Received`,
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * @param to The address that will own the minted token
     * @param tokenId uint256 ID of the token to be minted
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Internal function to safely mint a new token.
     * Reverts if the given token ID already exists.
     * If the target address is a contract, it must implement `onERC721Received`,
     * which is called upon a safe transfer, and return the magic value
     * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
     * the transfer is reverted.
     * @param to The address that will own the minted token
     * @param tokenId uint256 ID of the token to be minted
     * @param _data bytes data to send along with a safe transfer check
     */
    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Internal function to mint a new token.
     * Reverts if the given token ID already exists.
     * @param to The address that will own the minted token
     * @param tokenId uint256 ID of the token to be minted
     */
    function _mint(address to, uint256 tokenId) internal {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _tokenOwner[tokenId] = to;
        _ownedTokensCount[to].increment();

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Internal function to burn a specific token.
     * Reverts if the token does not exist.
     * Deprecated, use {_burn} instead.
     * @param owner owner of the token to burn
     * @param tokenId uint256 ID of the token being burned
     */
    function _burn(address owner, uint256 tokenId) internal {
        require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");

        _clearApproval(tokenId);

        _ownedTokensCount[owner].decrement();
        _tokenOwner[tokenId] = address(0);

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Internal function to burn a specific token.
     * Reverts if the token does not exist.
     * @param tokenId uint256 ID of the token being burned
     */
    function _burn(uint256 tokenId) internal {
        _burn(ownerOf(tokenId), tokenId);
    }

    /**
     * @dev Internal function to transfer ownership of a given token ID to another address.
     * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function _transferFrom(address from, address to, uint256 tokenId) internal {
        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _clearApproval(tokenId);

        _ownedTokensCount[from].decrement();
        _ownedTokensCount[to].increment();

        _tokenOwner[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * This is an internal detail of the `ERC721` contract and its use is deprecated.
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        internal returns (bool)
    {
        if (!to.isContract()) {
            return true;
        }
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = to.call(abi.encodeWithSelector(
            IERC721Receiver(to).onERC721Received.selector,
            _msgSender(),
            from,
            tokenId,
            _data
        ));
        if (!success) {
            if (returndata.length > 0) {
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert("ERC721: transfer to non ERC721Receiver implementer");
            }
        } else {
            bytes4 retval = abi.decode(returndata, (bytes4));
            return (retval == _ERC721_RECEIVED);
        }
    }

    /**
     * @dev Private function to clear current approval of a given token ID.
     * @param tokenId uint256 ID of the token to be transferred
     */
    function _clearApproval(uint256 tokenId) private {
        if (_tokenApprovals[tokenId] != address(0)) {
            _tokenApprovals[tokenId] = address(0);
        }
    }
}

File 17 of 64 : IERC721.sol
pragma solidity ^0.5.0;

import "../../introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
contract IERC721 is IERC165 {
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of NFTs in `owner`'s account.
     */
    function balanceOf(address owner) public view returns (uint256 balance);

    /**
     * @dev Returns the owner of the NFT specified by `tokenId`.
     */
    function ownerOf(uint256 tokenId) public view returns (address owner);

    /**
     * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
     * another (`to`).
     *
     *
     *
     * Requirements:
     * - `from`, `to` cannot be zero.
     * - `tokenId` must be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this
     * NFT by either {approve} or {setApprovalForAll}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public;
    /**
     * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
     * another (`to`).
     *
     * Requirements:
     * - If the caller is not `from`, it must be approved to move this NFT by
     * either {approve} or {setApprovalForAll}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public;
    function approve(address to, uint256 tokenId) public;
    function getApproved(uint256 tokenId) public view returns (address operator);

    function setApprovalForAll(address operator, bool _approved) public;
    function isApprovedForAll(address owner, address operator) public view returns (bool);


    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}

File 18 of 64 : IERC165.sol
pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 19 of 64 : IERC721Receiver.sol
pragma solidity ^0.5.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
contract IERC721Receiver {
    /**
     * @notice Handle the receipt of an NFT
     * @dev The ERC721 smart contract calls this function on the recipient
     * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
     * otherwise the caller will revert the transaction. The selector to be
     * returned can be obtained as `this.onERC721Received.selector`. This
     * function MAY throw to revert and reject the transfer.
     * Note: the ERC721 contract address is always the message sender.
     * @param operator The address which called `safeTransferFrom` function
     * @param from The address which previously owned the token
     * @param tokenId The NFT identifier which is being transferred
     * @param data Additional data with no specified format
     * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
    public returns (bytes4);
}

File 20 of 64 : Counters.sol
pragma solidity ^0.5.0;

import "../math/SafeMath.sol";

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
 * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
 * directly accessed.
 */
library Counters {
    using SafeMath for uint256;

    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        // The {SafeMath} overflow check can be skipped here, see the comment at the top
        counter._value += 1;
    }

    function decrement(Counter storage counter) internal {
        counter._value = counter._value.sub(1);
    }
}

File 21 of 64 : ERC165.sol
pragma solidity ^0.5.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () internal {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

File 22 of 64 : IERC721Metadata.sol
pragma solidity ^0.5.0;

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
contract IERC721Metadata is IERC721 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 23 of 64 : MPHMinter.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./MPHToken.sol";
import "../models/issuance/IMPHIssuanceModel.sol";
import "./Vesting.sol";

contract MPHMinter is Ownable {
    using Address for address;
    using SafeMath for uint256;

    mapping(address => bool) public poolWhitelist;

    modifier onlyWhitelistedPool {
        require(poolWhitelist[msg.sender], "MPHMinter: sender not whitelisted");
        _;
    }

    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
    event WhitelistPool(
        address indexed sender,
        address pool,
        bool isWhitelisted
    );
    event MintDepositorReward(
        address indexed sender,
        address indexed to,
        uint256 depositorReward
    );
    event TakeBackDepositorReward(
        address indexed sender,
        address indexed from,
        uint256 takeBackAmount
    );
    event MintFunderReward(
        address indexed sender,
        address indexed to,
        uint256 funderReward
    );

    /**
        External contracts
     */
    MPHToken public mph;
    address public govTreasury;
    address public devWallet;
    IMPHIssuanceModel public issuanceModel;
    Vesting public vesting;

    constructor(
        address _mph,
        address _govTreasury,
        address _devWallet,
        address _issuanceModel,
        address _vesting
    ) public {
        mph = MPHToken(_mph);
        govTreasury = _govTreasury;
        devWallet = _devWallet;
        issuanceModel = IMPHIssuanceModel(_issuanceModel);
        vesting = Vesting(_vesting);
    }

    /**
        @notice Mints the MPH reward to a depositor upon deposit.
        @param  to The depositor
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  depositPeriodInSeconds The deposit's lock period in seconds
        @param  interestAmount The deposit's fixed-rate interest amount in the pool's stablecoins
        @return depositorReward The MPH amount to mint to the depositor
     */
    function mintDepositorReward(
        address to,
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 interestAmount
    ) external onlyWhitelistedPool returns (uint256) {
        (
            uint256 depositorReward,
            uint256 devReward,
            uint256 govReward
        ) = issuanceModel.computeDepositorReward(
            msg.sender,
            depositAmount,
            depositPeriodInSeconds,
            interestAmount
        );
        if (depositorReward == 0 && devReward == 0 && govReward == 0) {
            return 0;
        }

        // mint and vest depositor reward
        mph.ownerMint(address(this), depositorReward);
        uint256 vestPeriodInSeconds = issuanceModel
            .poolDepositorRewardVestPeriod(msg.sender);
        if (vestPeriodInSeconds == 0) {
            // no vesting, transfer to `to`
            mph.transfer(to, depositorReward);
        } else {
            // vest the MPH to `to`
            mph.increaseAllowance(address(vesting), depositorReward);
            vesting.vest(to, depositorReward, vestPeriodInSeconds);
        }

        mph.ownerMint(devWallet, devReward);
        mph.ownerMint(govTreasury, govReward);

        emit MintDepositorReward(msg.sender, to, depositorReward);

        return depositorReward;
    }

    /**
        @notice Takes back MPH from depositor upon withdrawal.
                If takeBackAmount > devReward + govReward, the extra MPH should be burnt.
        @param  from The depositor
        @param  mintMPHAmount The MPH amount originally minted to the depositor as reward
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return takeBackAmount The MPH amount to take back from the depositor
     */
    function takeBackDepositorReward(
        address from,
        uint256 mintMPHAmount,
        bool early
    ) external onlyWhitelistedPool returns (uint256) {
        (
            uint256 takeBackAmount,
            uint256 devReward,
            uint256 govReward
        ) = issuanceModel.computeTakeBackDepositorRewardAmount(
            msg.sender,
            mintMPHAmount,
            early
        );
        if (takeBackAmount == 0 && devReward == 0 && govReward == 0) {
            return 0;
        }
        require(
            takeBackAmount >= devReward.add(govReward),
            "MPHMinter: takeBackAmount < devReward + govReward"
        );
        mph.transferFrom(from, address(this), takeBackAmount);
        mph.transfer(devWallet, devReward);
        mph.transfer(govTreasury, govReward);
        mph.burn(takeBackAmount.sub(devReward).sub(govReward));

        emit TakeBackDepositorReward(msg.sender, from, takeBackAmount);

        return takeBackAmount;
    }

    /**
        @notice Mints the MPH reward to a deficit funder upon withdrawal of an underlying deposit.
        @param  to The funder
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  fundingCreationTimestamp The timestamp of the funding's creation, in seconds
        @param  maturationTimestamp The maturation timestamp of the deposit, in seconds
        @param  interestPayoutAmount The interest payout amount to the funder, in the pool's stablecoins.
                                     Includes the interest from other funded deposits.
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return funderReward The MPH amount to mint to the funder
     */
    function mintFunderReward(
        address to,
        uint256 depositAmount,
        uint256 fundingCreationTimestamp,
        uint256 maturationTimestamp,
        uint256 interestPayoutAmount,
        bool early
    ) external onlyWhitelistedPool returns (uint256) {
        (
            uint256 funderReward,
            uint256 devReward,
            uint256 govReward
        ) = issuanceModel.computeFunderReward(
            msg.sender,
            depositAmount,
            fundingCreationTimestamp,
            maturationTimestamp,
            interestPayoutAmount,
            early
        );
        if (funderReward == 0 && devReward == 0 && govReward == 0) {
            return 0;
        }

        // mint and vest funder reward
        mph.ownerMint(address(this), funderReward);
        uint256 vestPeriodInSeconds = issuanceModel.poolFunderRewardVestPeriod(
            msg.sender
        );
        if (vestPeriodInSeconds == 0) {
            // no vesting, transfer to `to`
            mph.transfer(to, funderReward);
        } else {
            // vest the MPH to `to`
            mph.increaseAllowance(address(vesting), funderReward);
            vesting.vest(to, funderReward, vestPeriodInSeconds);
        }
        mph.ownerMint(devWallet, devReward);
        mph.ownerMint(govTreasury, govReward);

        emit MintFunderReward(msg.sender, to, funderReward);

        return funderReward;
    }

    /**
        Param setters
     */
    function setGovTreasury(address newValue) external onlyOwner {
        require(newValue != address(0), "MPHMinter: 0 address");
        govTreasury = newValue;
        emit ESetParamAddress(msg.sender, "govTreasury", newValue);
    }

    function setDevWallet(address newValue) external onlyOwner {
        require(newValue != address(0), "MPHMinter: 0 address");
        devWallet = newValue;
        emit ESetParamAddress(msg.sender, "devWallet", newValue);
    }

    function setMPHTokenOwner(address newValue) external onlyOwner {
        require(newValue != address(0), "MPHMinter: 0 address");
        mph.transferOwnership(newValue);
        emit ESetParamAddress(msg.sender, "mphTokenOwner", newValue);
    }

    function setMPHTokenOwnerToZero() external onlyOwner {
        mph.renounceOwnership();
        emit ESetParamAddress(msg.sender, "mphTokenOwner", address(0));
    }

    function setIssuanceModel(address newValue) external onlyOwner {
        require(newValue.isContract(), "MPHMinter: not contract");
        issuanceModel = IMPHIssuanceModel(newValue);
        emit ESetParamAddress(msg.sender, "issuanceModel", newValue);
    }

    function setVesting(address newValue) external onlyOwner {
        require(newValue.isContract(), "MPHMinter: not contract");
        vesting = Vesting(newValue);
        emit ESetParamAddress(msg.sender, "vesting", newValue);
    }

    function setPoolWhitelist(address pool, bool isWhitelisted)
        external
        onlyOwner
    {
        require(pool.isContract(), "MPHMinter: pool not contract");
        poolWhitelist[pool] = isWhitelisted;
        emit WhitelistPool(msg.sender, pool, isWhitelisted);
    }
}

File 24 of 64 : MPHToken.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";

contract MPHToken is ERC20, ERC20Burnable, Ownable {
    string public constant name = "88mph.app";
    string public constant symbol = "MPH";
    uint8 public constant decimals = 18;
    
    bool public initialized;

    function init() public {
        require(!initialized, "MPHToken: initialized");
        initialized = true;

        _transferOwnership(msg.sender);
    }

    function ownerMint(address account, uint256 amount)
        public
        onlyOwner
        returns (bool)
    {
        _mint(account, amount);
        return true;
    }
}

File 25 of 64 : ERC20Burnable.sol
pragma solidity ^0.5.0;

import "../../GSN/Context.sol";
import "./ERC20.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev See {ERC20-_burnFrom}.
     */
    function burnFrom(address account, uint256 amount) public {
        _burnFrom(account, amount);
    }
}

File 26 of 64 : IMPHIssuanceModel.sol
pragma solidity 0.5.17;

interface IMPHIssuanceModel {
    /**
        @notice Computes the MPH amount to reward to a depositor upon deposit.
        @param  pool The DInterest pool trying to mint reward
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  depositPeriodInSeconds The deposit's lock period in seconds
        @param  interestAmount The deposit's fixed-rate interest amount in the pool's stablecoins
        @return depositorReward The MPH amount to mint to the depositor
                devReward The MPH amount to mint to the dev wallet
                govReward The MPH amount to mint to the gov treasury
     */
    function computeDepositorReward(
        address pool,
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 interestAmount
    )
        external
        view
        returns (
            uint256 depositorReward,
            uint256 devReward,
            uint256 govReward
        );

    /**
        @notice Computes the MPH amount to take back from a depositor upon withdrawal.
                If takeBackAmount > devReward + govReward, the extra MPH should be burnt.
        @param  pool The DInterest pool trying to mint reward
        @param  mintMPHAmount The MPH amount originally minted to the depositor as reward
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return takeBackAmount The MPH amount to take back from the depositor
                devReward The MPH amount from takeBackAmount to send to the dev wallet
                govReward The MPH amount from takeBackAmount to send to the gov treasury
     */
    function computeTakeBackDepositorRewardAmount(
        address pool,
        uint256 mintMPHAmount,
        bool early
    )
        external
        view
        returns (
            uint256 takeBackAmount,
            uint256 devReward,
            uint256 govReward
        );

    /**
        @notice Computes the MPH amount to reward to a deficit funder upon withdrawal of an underlying deposit.
        @param  pool The DInterest pool trying to mint reward
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  fundingCreationTimestamp The timestamp of the funding's creation, in seconds
        @param  maturationTimestamp The maturation timestamp of the deposit, in seconds
        @param  interestPayoutAmount The interest payout amount to the funder, in the pool's stablecoins.
                                     Includes the interest from other funded deposits.
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return funderReward The MPH amount to mint to the funder
                devReward The MPH amount to mint to the dev wallet
                govReward The MPH amount to mint to the gov treasury
     */
    function computeFunderReward(
        address pool,
        uint256 depositAmount,
        uint256 fundingCreationTimestamp,
        uint256 maturationTimestamp,
        uint256 interestPayoutAmount,
        bool early
    )
        external
        view
        returns (
            uint256 funderReward,
            uint256 devReward,
            uint256 govReward
        );

    /**
        @notice The period over which the depositor reward will be vested, in seconds.
     */
    function poolDepositorRewardVestPeriod(address pool)
        external
        view
        returns (uint256 vestPeriodInSeconds);

    /**
        @notice The period over which the funder reward will be vested, in seconds.
     */
    function poolFunderRewardVestPeriod(address pool)
        external
        view
        returns (uint256 vestPeriodInSeconds);
}

File 27 of 64 : Vesting.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";

contract Vesting {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    struct Vest {
        uint256 amount;
        uint256 vestPeriodInSeconds;
        uint256 creationTimestamp;
        uint256 withdrawnAmount;
    }
    mapping(address => Vest[]) public accountVestList;

    IERC20 public token;

    constructor(address _token) public {
        token = IERC20(_token);
    }

    function vest(
        address to,
        uint256 amount,
        uint256 vestPeriodInSeconds
    ) external returns (uint256 vestIdx) {
        require(vestPeriodInSeconds > 0, "Vesting: vestPeriodInSeconds == 0");

        // transfer `amount` tokens from `msg.sender`
        token.safeTransferFrom(msg.sender, address(this), amount);

        // create vest object
        vestIdx = accountVestList[to].length;
        accountVestList[to].push(
            Vest({
                amount: amount,
                vestPeriodInSeconds: vestPeriodInSeconds,
                creationTimestamp: now,
                withdrawnAmount: 0
            })
        );
    }

    function withdrawVested(address account, uint256 vestIdx)
        external
        returns (uint256 withdrawnAmount)
    {
        // compute withdrawable amount
        withdrawnAmount = _getVestWithdrawableAmount(account, vestIdx);
        if (withdrawnAmount == 0) {
            return 0;
        }

        // update vest object
        uint256 recordedWithdrawnAmount = accountVestList[account][vestIdx]
            .withdrawnAmount;
        accountVestList[account][vestIdx]
            .withdrawnAmount = recordedWithdrawnAmount.add(withdrawnAmount);

        // transfer tokens to vest recipient
        token.safeTransfer(account, withdrawnAmount);
    }

    function getVestWithdrawableAmount(address account, uint256 vestIdx)
        external
        view
        returns (uint256)
    {
        return _getVestWithdrawableAmount(account, vestIdx);
    }

    function _getVestWithdrawableAmount(address account, uint256 vestIdx)
        internal
        view
        returns (uint256)
    {
        // read vest data
        Vest storage vest = accountVestList[account][vestIdx];
        uint256 vestFullAmount = vest.amount;
        uint256 vestCreationTimestamp = vest.creationTimestamp;
        uint256 vestPeriodInSeconds = vest.vestPeriodInSeconds;

        // compute vested amount
        uint256 vestedAmount;
        if (now >= vestCreationTimestamp.add(vestPeriodInSeconds)) {
            // vest period has passed, fully withdrawable
            vestedAmount = vestFullAmount;
        } else {
            // vest period has not passed, linearly unlock
            vestedAmount = vestFullAmount
                .mul(now.sub(vestCreationTimestamp))
                .div(vestPeriodInSeconds);
        }

        // deduct already withdrawn amount and return
        return vestedAmount.sub(vest.withdrawnAmount);
    }
}

File 28 of 64 : IInterestOracle.sol
pragma solidity 0.5.17;

interface IInterestOracle {
    function updateAndQuery() external returns (bool updated, uint256 value);

    function query() external view returns (uint256 value);

    function moneyMarket() external view returns (address);
}

File 29 of 64 : ATokenMock.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../libs/DecMath.sol";

contract ATokenMock is ERC20, ERC20Detailed {
    using SafeMath for uint256;
    using DecMath for uint256;

    uint256 internal constant YEAR = 31556952; // Number of seconds in one Gregorian calendar year (365.2425 days)

    ERC20 public dai;
    uint256 public liquidityRate;
    uint256 public normalizedIncome;
    address[] public users;
    mapping(address => bool) public isUser;

    constructor(address _dai)
        public
        ERC20Detailed("aDAI", "aDAI", 18)
    {
        dai = ERC20(_dai);

        liquidityRate = 10 ** 26; // 10% APY
        normalizedIncome = 10 ** 27;
    }

    function mint(address _user, uint256 _amount) external {
        _mint(_user, _amount);
        if (!isUser[_user]) {
            users.push(_user);
            isUser[_user] = true;
        }
    }

    function burn(address _user, uint256 _amount) external {
        _burn(_user, _amount);
    }

    function mintInterest(uint256 _seconds) external {
        uint256 interest;
        address user;
        for (uint256 i = 0; i < users.length; i++) {
            user = users[i];
            interest = balanceOf(user).mul(_seconds).mul(liquidityRate).div(YEAR.mul(10**27));
            _mint(user, interest);
        }
        normalizedIncome = normalizedIncome.mul(_seconds).mul(liquidityRate).div(YEAR.mul(10**27)).add(normalizedIncome);
    }

    function setLiquidityRate(uint256 _liquidityRate) external {
        liquidityRate = _liquidityRate;
    }
}

File 30 of 64 : ERC20Detailed.sol
pragma solidity ^0.5.0;

import "./IERC20.sol";

/**
 * @dev Optional functions from the ERC20 standard.
 */
contract ERC20Detailed is IERC20 {
    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
     * these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name, string memory symbol, uint8 decimals) public {
        _name = name;
        _symbol = symbol;
        _decimals = decimals;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view 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.
     *
     * 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 returns (uint8) {
        return _decimals;
    }
}

File 31 of 64 : CERC20Mock.sol
/**
    Modified from https://github.com/bugduino/idle-contracts/blob/master/contracts/mocks/cDAIMock.sol
    at commit b85dafa8e55e053cb2d403fc4b28cfe86f2116d4

    Original license:
    Copyright 2020 Idle Labs Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

pragma solidity 0.5.17;

// interfaces
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";


contract CERC20Mock is ERC20, ERC20Detailed {
    address public dai;

    uint256 internal _supplyRate;
    uint256 internal _exchangeRate;

    constructor(address _dai) public ERC20Detailed("cDAI", "cDAI", 8) {
        dai = _dai;
        uint256 daiDecimals = ERC20Detailed(_dai).decimals();
        _exchangeRate = 2 * (10**(daiDecimals + 8)); // 1 cDAI = 0.02 DAI
        _supplyRate = 45290900000; // 10% supply rate per year
    }

    function mint(uint256 amount) external returns (uint256) {
        require(
            ERC20(dai).transferFrom(msg.sender, address(this), amount),
            "Error during transferFrom"
        ); // 1 DAI
        _mint(msg.sender, (amount * 10**18) / _exchangeRate);
        return 0;
    }

    function redeemUnderlying(uint256 amount) external returns (uint256) {
        _burn(msg.sender, (amount * 10**18) / _exchangeRate);
        require(
            ERC20(dai).transfer(msg.sender, amount),
            "Error during transfer"
        ); // 1 DAI
        return 0;
    }

    function exchangeRateStored() external view returns (uint256) {
        return _exchangeRate;
    }

    function exchangeRateCurrent() external view returns (uint256) {
        return _exchangeRate;
    }

    function _setExchangeRateStored(uint256 _rate) external returns (uint256) {
        _exchangeRate = _rate;
    }

    function supplyRatePerBlock() external view returns (uint256) {
        return _supplyRate;
    }

    function _setSupplyRatePerBlock(uint256 _rate) external {
        _supplyRate = _rate;
    }
}

File 32 of 64 : ComptrollerMock.sol
pragma solidity 0.5.17;

// interfaces
import "./ERC20Mock.sol";

contract ComptrollerMock {
    uint256 public constant CLAIM_AMOUNT = 10**18;
    ERC20Mock public comp;

    constructor (address _comp) public {
        comp = ERC20Mock(_comp);
    }

    function claimComp(address holder) external {
        comp.mint(holder, CLAIM_AMOUNT);
    }

    function getCompAddress() external view returns (address) {
        return address(comp);
    }
}

File 33 of 64 : ERC20Mock.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";

contract ERC20Mock is ERC20, ERC20Detailed("", "", 6) {
    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}

File 34 of 64 : HarvestStakingMock.sol
/*
   ____            __   __        __   _
  / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
 _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
/___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
     /___/
* Synthetix: Rewards.sol
*
* Docs: https://docs.synthetix.io/
*
*
* MIT License
* ===========
*
* Copyright (c) 2020 Synthetix
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";

contract IRewardDistributionRecipient is Ownable {
    mapping(address => bool) public isRewardDistribution;

    function notifyRewardAmount(uint256 reward) external;

    modifier onlyRewardDistribution() {
        require(
            isRewardDistribution[_msgSender()],
            "Caller is not reward distribution"
        );
        _;
    }

    function setRewardDistribution(
        address _rewardDistribution,
        bool _isRewardDistribution
    ) external onlyOwner {
        isRewardDistribution[_rewardDistribution] = _isRewardDistribution;
    }
}

contract LPTokenWrapper {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    IERC20 public stakeToken;

    uint256 private _totalSupply;

    mapping(address => uint256) private _balances;

    constructor(address _stakeToken) public {
        stakeToken = IERC20(_stakeToken);
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function stake(uint256 amount) public {
        _totalSupply = _totalSupply.add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        stakeToken.safeTransferFrom(msg.sender, address(this), amount);
    }

    function withdraw(uint256 amount) public {
        _totalSupply = _totalSupply.sub(amount);
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        stakeToken.safeTransfer(msg.sender, amount);
    }
}

contract HarvestStakingMock is LPTokenWrapper, IRewardDistributionRecipient {
    IERC20 public rewardToken;
    uint256 public constant DURATION = 7 days;

    uint256 public starttime;
    uint256 public periodFinish = 0;
    uint256 public rewardRate = 0;
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;

    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards;

    event RewardAdded(uint256 reward);
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);

    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    modifier checkStart {
        require(block.timestamp >= starttime, "Rewards: not start");
        _;
    }

    constructor(
        address _stakeToken,
        address _rewardToken,
        uint256 _starttime
    ) public LPTokenWrapper(_stakeToken) {
        rewardToken = IERC20(_rewardToken);
        starttime = _starttime;
    }

    function lastTimeRewardApplicable() public view returns (uint256) {
        return Math.min(block.timestamp, periodFinish);
    }

    function rewardPerToken() public view returns (uint256) {
        if (totalSupply() == 0) {
            return rewardPerTokenStored;
        }
        return
            rewardPerTokenStored.add(
                lastTimeRewardApplicable()
                    .sub(lastUpdateTime)
                    .mul(rewardRate)
                    .mul(1e18)
                    .div(totalSupply())
            );
    }

    function earned(address account) public view returns (uint256) {
        return
            balanceOf(account)
                .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                .div(1e18)
                .add(rewards[account]);
    }

    // stake visibility is public as overriding LPTokenWrapper's stake() function
    function stake(uint256 amount) public updateReward(msg.sender) checkStart {
        require(amount > 0, "Rewards: cannot stake 0");
        super.stake(amount);
        emit Staked(msg.sender, amount);
    }

    function withdraw(uint256 amount)
        public
        updateReward(msg.sender)
        checkStart
    {
        require(amount > 0, "Rewards: cannot withdraw 0");
        super.withdraw(amount);
        emit Withdrawn(msg.sender, amount);
    }

    function exit() external {
        withdraw(balanceOf(msg.sender));
        getReward();
    }

    function getReward() public updateReward(msg.sender) checkStart {
        uint256 reward = earned(msg.sender);
        if (reward > 0) {
            rewards[msg.sender] = 0;
            rewardToken.safeTransfer(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
        }
    }

    function notifyRewardAmount(uint256 reward)
        external
        onlyRewardDistribution
        updateReward(address(0))
    {
        // https://sips.synthetix.io/sips/sip-77
        require(reward > 0, "Rewards: reward == 0");
        require(
            reward < uint256(-1) / 10**18,
            "Rewards: rewards too large, would lock"
        );
        if (block.timestamp > starttime) {
            if (block.timestamp >= periodFinish) {
                rewardRate = reward.div(DURATION);
            } else {
                uint256 remaining = periodFinish.sub(block.timestamp);
                uint256 leftover = remaining.mul(rewardRate);
                rewardRate = reward.add(leftover).div(DURATION);
            }
            lastUpdateTime = block.timestamp;
            periodFinish = block.timestamp.add(DURATION);
            emit RewardAdded(reward);
        } else {
            rewardRate = reward.div(DURATION);
            lastUpdateTime = starttime;
            periodFinish = starttime.add(DURATION);
            emit RewardAdded(reward);
        }
    }
}

File 35 of 64 : Math.sol
pragma solidity ^0.5.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

File 36 of 64 : LendingPoolAddressesProviderMock.sol
pragma solidity 0.5.17;

contract LendingPoolAddressesProviderMock {
    address internal pool;
    address internal core;

    function getLendingPool() external view returns (address) {
        return pool;
    }

    function setLendingPoolImpl(address _pool) external {
        pool = _pool;
    }
}

File 37 of 64 : LendingPoolMock.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./ATokenMock.sol";

contract LendingPoolMock {
    mapping(address => address) internal reserveAToken;

    function setReserveAToken(address _reserve, address _aTokenAddress)
        external
    {
        reserveAToken[_reserve] = _aTokenAddress;
    }

    function deposit(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16
    ) external {
        // Transfer asset
        ERC20 token = ERC20(asset);
        token.transferFrom(msg.sender, address(this), amount);

        // Mint aTokens
        address aTokenAddress = reserveAToken[asset];
        ATokenMock aToken = ATokenMock(aTokenAddress);
        aToken.mint(onBehalfOf, amount);
    }

    function withdraw(
        address asset,
        uint256 amount,
        address to
    ) external returns (uint256) {
        // Burn aTokens
        address aTokenAddress = reserveAToken[asset];
        ATokenMock aToken = ATokenMock(aTokenAddress);
        aToken.burn(msg.sender, amount);

        // Transfer asset
        ERC20 token = ERC20(asset);
        token.transfer(to, amount);
    }

    // The equivalent of exchangeRateStored() for Compound cTokens
    function getReserveNormalizedIncome(address asset)
        external
        view
        returns (uint256)
    {
        address aTokenAddress = reserveAToken[asset];
        ATokenMock aToken = ATokenMock(aTokenAddress);
        return aToken.normalizedIncome();
    }
}

File 38 of 64 : VaultMock.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../libs/DecMath.sol";

contract VaultMock is ERC20, ERC20Detailed {
    using SafeMath for uint256;
    using DecMath for uint256;

    ERC20 public underlying;

    constructor(address _underlying) public ERC20Detailed("yUSD", "yUSD", 18) {
        underlying = ERC20(_underlying);
    }

    function deposit(uint256 tokenAmount) public {
        uint256 sharePrice = getPricePerFullShare();
        _mint(msg.sender, tokenAmount.decdiv(sharePrice));

        underlying.transferFrom(msg.sender, address(this), tokenAmount);
    }

    function withdraw(uint256 sharesAmount) public {
        uint256 sharePrice = getPricePerFullShare();
        uint256 underlyingAmount = sharesAmount.decmul(sharePrice);
        _burn(msg.sender, sharesAmount);

        underlying.transfer(msg.sender, underlyingAmount);
    }

    function getPricePerFullShare() public view returns (uint256) {
        uint256 _totalSupply = totalSupply();
        if (_totalSupply == 0) {
            return 10**18;
        }
        return underlying.balanceOf(address(this)).decdiv(_totalSupply);
    }
}

File 39 of 64 : PercentageFeeModel.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "./IFeeModel.sol";

contract PercentageFeeModel is IFeeModel, Ownable {
    using SafeMath for uint256;

    address payable public beneficiary;

    event SetBeneficiary(address newBeneficiary);

    constructor(address payable _beneficiary) public {
        beneficiary = _beneficiary;
    }

    function getFee(uint256 _txAmount)
        external
        pure
        returns (uint256 _feeAmount)
    {
        _feeAmount = _txAmount.div(10); // Precision is decreased by 1 decimal place
    }

    function setBeneficiary(address payable newValue) external onlyOwner {
        require(newValue != address(0), "PercentageFeeModel: 0 address");
        beneficiary = newValue;
        emit SetBeneficiary(newValue);
    }
}

File 40 of 64 : EMAOracle.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../moneymarkets/IMoneyMarket.sol";
import "../../libs/DecMath.sol";
import "./IInterestOracle.sol";

contract EMAOracle is IInterestOracle {
    using SafeMath for uint256;
    using DecMath for uint256;

    uint256 internal constant PRECISION = 10**18;

    /**
        Immutable parameters
     */
    uint256 public UPDATE_INTERVAL;
    uint256 public UPDATE_MULTIPLIER;
    uint256 public ONE_MINUS_UPDATE_MULTIPLIER;

    /**
        Public variables
     */
    uint256 public emaStored;
    uint256 public lastIncomeIndex;
    uint256 public lastUpdateTimestamp;

    /**
        External contracts
     */
    IMoneyMarket public moneyMarket;

    constructor(
        uint256 _emaInitial,
        uint256 _updateInterval,
        uint256 _smoothingFactor,
        uint256 _averageWindowInIntervals,
        address _moneyMarket
    ) public {
        emaStored = _emaInitial;
        UPDATE_INTERVAL = _updateInterval;
        lastUpdateTimestamp = now;

        uint256 updateMultiplier = _smoothingFactor.div(_averageWindowInIntervals.add(1));
        UPDATE_MULTIPLIER = updateMultiplier;
        ONE_MINUS_UPDATE_MULTIPLIER = PRECISION.sub(updateMultiplier);

        moneyMarket = IMoneyMarket(_moneyMarket);
        lastIncomeIndex = moneyMarket.incomeIndex();
    }

    function updateAndQuery() public returns (bool updated, uint256 value) {
        uint256 timeElapsed = now - lastUpdateTimestamp;
        if (timeElapsed < UPDATE_INTERVAL) {
            return (false, emaStored);
        }

        // save gas by loading storage variables to memory
        uint256 _lastIncomeIndex = lastIncomeIndex;
        uint256 _emaStored = emaStored;

        uint256 newIncomeIndex = moneyMarket.incomeIndex();
        uint256 incomingValue = newIncomeIndex.sub(_lastIncomeIndex).decdiv(_lastIncomeIndex).div(timeElapsed);

        updated = true;
        value = incomingValue.mul(UPDATE_MULTIPLIER).add(_emaStored.mul(ONE_MINUS_UPDATE_MULTIPLIER)).div(PRECISION);
        emaStored = value;
        lastIncomeIndex = newIncomeIndex;
        lastUpdateTimestamp = now;
    }

    function query() public view returns (uint256 value) {
        return emaStored;
    }
}

File 41 of 64 : LinearInterestModel.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../libs/DecMath.sol";

contract LinearInterestModel {
    using SafeMath for uint256;
    using DecMath for uint256;

    uint256 public constant PRECISION = 10**18;
    uint256 public IRMultiplier;

    constructor(uint256 _IRMultiplier) public {
        IRMultiplier = _IRMultiplier;
    }

    function calculateInterestAmount(
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 moneyMarketInterestRatePerSecond,
        bool, /*surplusIsNegative*/
        uint256 /*surplusAmount*/
    ) external view returns (uint256 interestAmount) {
        // interestAmount = depositAmount * moneyMarketInterestRatePerSecond * IRMultiplier * depositPeriodInSeconds
        interestAmount = depositAmount
            .mul(PRECISION)
            .decmul(moneyMarketInterestRatePerSecond)
            .decmul(IRMultiplier)
            .mul(depositPeriodInSeconds)
            .div(PRECISION);
    }
}

File 42 of 64 : MPHIssuanceModel01.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../../libs/DecMath.sol";
import "./IMPHIssuanceModel.sol";

contract MPHIssuanceModel01 is Ownable, IMPHIssuanceModel {
    using Address for address;
    using DecMath for uint256;
    using SafeMath for uint256;

    uint256 internal constant PRECISION = 10**18;

    /**
        @notice The multiplier applied when minting MPH for a pool's depositor reward.
                Unit is MPH-wei per depositToken-wei per second. (wei here is the smallest decimal place)
                Scaled by 10^18.
                NOTE: The depositToken's decimals matter! 
     */
    mapping(address => uint256) public poolDepositorRewardMintMultiplier;
    /**
        @notice The multiplier applied when taking back MPH from depositors upon withdrawal.
                No unit, is a proportion between 0 and 1.
                Scaled by 10^18.
     */
    mapping(address => uint256) public poolDepositorRewardTakeBackMultiplier;
    /**
        @notice The multiplier applied when minting MPH for a pool's funder reward.
                Unit is MPH-wei per depositToken-wei per second. (wei here is the smallest decimal place)
                Scaled by 10^18.
                NOTE: The depositToken's decimals matter! 
     */
    mapping(address => uint256) public poolFunderRewardMultiplier;
    /**
        @notice The period over which the depositor reward will be vested, in seconds.
     */
    mapping(address => uint256) public poolDepositorRewardVestPeriod;
    /**
        @notice The period over which the funder reward will be vested, in seconds.
     */
    mapping(address => uint256) public poolFunderRewardVestPeriod;

    /**
        @notice Multiplier used for calculating dev reward
     */
    uint256 public devRewardMultiplier;

    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
    event ESetParamUint(
        address indexed sender,
        string indexed paramName,
        address indexed pool,
        uint256 newValue
    );

    constructor(uint256 _devRewardMultiplier) public {
        devRewardMultiplier = _devRewardMultiplier;
    }

    /**
        @notice Computes the MPH amount to reward to a depositor upon deposit.
        @param  pool The DInterest pool trying to mint reward
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  depositPeriodInSeconds The deposit's lock period in seconds
        @param  interestAmount The deposit's fixed-rate interest amount in the pool's stablecoins
        @return depositorReward The MPH amount to mint to the depositor
                devReward The MPH amount to mint to the dev wallet
                govReward The MPH amount to mint to the gov treasury
     */
    function computeDepositorReward(
        address pool,
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 interestAmount
    )
        external
        view
        returns (
            uint256 depositorReward,
            uint256 devReward,
            uint256 govReward
        )
    {
        uint256 mintAmount = depositAmount.mul(depositPeriodInSeconds).decmul(
            poolDepositorRewardMintMultiplier[pool]
        );
        depositorReward = mintAmount;
        devReward = mintAmount.decmul(devRewardMultiplier);
        govReward = 0;
    }

    /**
        @notice Computes the MPH amount to take back from a depositor upon withdrawal.
                If takeBackAmount > devReward + govReward, the extra MPH should be burnt.
        @param  pool The DInterest pool trying to mint reward
        @param  mintMPHAmount The MPH amount originally minted to the depositor as reward
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return takeBackAmount The MPH amount to take back from the depositor
                devReward The MPH amount from takeBackAmount to send to the dev wallet
                govReward The MPH amount from takeBackAmount to send to the gov treasury
     */
    function computeTakeBackDepositorRewardAmount(
        address pool,
        uint256 mintMPHAmount,
        bool early
    )
        external
        view
        returns (
            uint256 takeBackAmount,
            uint256 devReward,
            uint256 govReward
        )
    {
        takeBackAmount = early
            ? mintMPHAmount
            : mintMPHAmount.decmul(poolDepositorRewardTakeBackMultiplier[pool]);
        devReward = 0;
        govReward = early ? 0 : takeBackAmount;
    }

    /**
        @notice Computes the MPH amount to reward to a deficit funder upon withdrawal of an underlying deposit.
        @param  pool The DInterest pool trying to mint reward
        @param  depositAmount The deposit amount in the pool's stablecoins
        @param  fundingCreationTimestamp The timestamp of the funding's creation, in seconds
        @param  maturationTimestamp The maturation timestamp of the deposit, in seconds
        @param  interestPayoutAmount The interest payout amount to the funder, in the pool's stablecoins.
                                     Includes the interest from other funded deposits.
        @param  early True if the deposit is withdrawn early, false if the deposit is mature
        @return funderReward The MPH amount to mint to the funder
                devReward The MPH amount to mint to the dev wallet
                govReward The MPH amount to mint to the gov treasury
     */
    function computeFunderReward(
        address pool,
        uint256 depositAmount,
        uint256 fundingCreationTimestamp,
        uint256 maturationTimestamp,
        uint256 interestPayoutAmount,
        bool early
    )
        external
        view
        returns (
            uint256 funderReward,
            uint256 devReward,
            uint256 govReward
        )
    {
        if (early) {
            return (0, 0, 0);
        }
        funderReward = maturationTimestamp > fundingCreationTimestamp
            ? depositAmount
                .mul(maturationTimestamp.sub(fundingCreationTimestamp))
                .decmul(poolFunderRewardMultiplier[pool])
            : 0;
        devReward = funderReward.decmul(devRewardMultiplier);
        govReward = 0;
    }

    /**
        Param setters
     */

    function setPoolDepositorRewardMintMultiplier(
        address pool,
        uint256 newMultiplier
    ) external onlyOwner {
        require(pool.isContract(), "MPHIssuanceModel: pool not contract");
        poolDepositorRewardMintMultiplier[pool] = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "poolDepositorRewardMintMultiplier",
            pool,
            newMultiplier
        );
    }

    function setPoolDepositorRewardTakeBackMultiplier(
        address pool,
        uint256 newMultiplier
    ) external onlyOwner {
        require(pool.isContract(), "MPHIssuanceModel: pool not contract");
        require(
            newMultiplier <= PRECISION,
            "MPHIssuanceModel: invalid multiplier"
        );
        poolDepositorRewardTakeBackMultiplier[pool] = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "poolDepositorRewardTakeBackMultiplier",
            pool,
            newMultiplier
        );
    }

    function setPoolFunderRewardMultiplier(address pool, uint256 newMultiplier)
        external
        onlyOwner
    {
        require(pool.isContract(), "MPHIssuanceModel: pool not contract");
        poolFunderRewardMultiplier[pool] = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "poolFunderRewardMultiplier",
            pool,
            newMultiplier
        );
    }

    function setPoolDepositorRewardVestPeriod(
        address pool,
        uint256 newVestPeriodInSeconds
    ) external onlyOwner {
        require(pool.isContract(), "MPHIssuanceModel: pool not contract");
        poolDepositorRewardVestPeriod[pool] = newVestPeriodInSeconds;
        emit ESetParamUint(
            msg.sender,
            "poolDepositorRewardVestPeriod",
            pool,
            newVestPeriodInSeconds
        );
    }

    function setPoolFunderRewardVestPeriod(
        address pool,
        uint256 newVestPeriodInSeconds
    ) external onlyOwner {
        require(pool.isContract(), "MPHIssuanceModel: pool not contract");
        poolFunderRewardVestPeriod[pool] = newVestPeriodInSeconds;
        emit ESetParamUint(
            msg.sender,
            "poolFunderRewardVestPeriod",
            pool,
            newVestPeriodInSeconds
        );
    }

    function setDevRewardMultiplier(uint256 newMultiplier) external onlyOwner {
        require(
            newMultiplier <= PRECISION,
            "MPHIssuanceModel: invalid multiplier"
        );
        devRewardMultiplier = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "devRewardMultiplier",
            address(0),
            newMultiplier
        );
    }
}

File 43 of 64 : AaveMarket.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../IMoneyMarket.sol";
import "./imports/ILendingPool.sol";
import "./imports/ILendingPoolAddressesProvider.sol";

contract AaveMarket is IMoneyMarket, Ownable {
    using SafeMath for uint256;
    using SafeERC20 for ERC20;
    using Address for address;

    uint16 internal constant REFERRALCODE = 20; // Aave referral program code

    ILendingPoolAddressesProvider public provider; // Used for fetching the current address of LendingPool
    ERC20 public stablecoin;
    ERC20 public aToken;

    constructor(
        address _provider,
        address _aToken,
        address _stablecoin
    ) public {
        // Verify input addresses
        require(
            _provider != address(0) &&
                _aToken != address(0) &&
                _stablecoin != address(0),
            "AaveMarket: An input address is 0"
        );
        require(
            _provider.isContract() &&
                _aToken.isContract() &&
                _stablecoin.isContract(),
            "AaveMarket: An input address is not a contract"
        );

        provider = ILendingPoolAddressesProvider(_provider);
        stablecoin = ERC20(_stablecoin);
        aToken = ERC20(_aToken);
    }

    function deposit(uint256 amount) external onlyOwner {
        require(amount > 0, "AaveMarket: amount is 0");

        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to lendingPool
        stablecoin.safeIncreaseAllowance(address(lendingPool), amount);

        // Deposit `amount` stablecoin to lendingPool
        lendingPool.deposit(
            address(stablecoin),
            amount,
            address(this),
            REFERRALCODE
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(amountInUnderlying > 0, "AaveMarket: amountInUnderlying is 0");

        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());

        // Redeem `amountInUnderlying` aToken, since 1 aToken = 1 stablecoin
        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        lendingPool.withdraw(
            address(stablecoin),
            amountInUnderlying,
            msg.sender
        );

        return amountInUnderlying;
    }

    function claimRewards() external {}

    function totalValue() external returns (uint256) {
        return aToken.balanceOf(address(this));
    }

    function incomeIndex() external returns (uint256) {
        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
        return lendingPool.getReserveNormalizedIncome(address(stablecoin));
    }

    function setRewards(address newValue) external {}
}

File 44 of 64 : ILendingPool.sol
pragma solidity 0.5.17;

// Aave lending pool interface
// Documentation: https://docs.aave.com/developers/the-core-protocol/lendingpool/ilendingpool
// refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
interface ILendingPool {
    /**
     * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to deposit
     * @param amount The amount to be deposited
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode
    ) external;

    /**
     * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to Address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(
        address asset,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve's normalized income
     */
    function getReserveNormalizedIncome(address asset)
        external
        view
        returns (uint256);
}

File 45 of 64 : ILendingPoolAddressesProvider.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.5.17;

/**
 * @title LendingPoolAddressesProvider contract
 * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
 * - Acting also as factory of proxies and admin of those, so with right to change its implementations
 * - Owned by the Aave Governance
 * @author Aave
 **/
interface ILendingPoolAddressesProvider {
  function getLendingPool() external view returns (address);
}

File 46 of 64 : CompoundERC20Market.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../IMoneyMarket.sol";
import "../../libs/DecMath.sol";
import "./imports/ICERC20.sol";
import "./imports/IComptroller.sol";

contract CompoundERC20Market is IMoneyMarket, Ownable {
    using DecMath for uint256;
    using SafeERC20 for ERC20;
    using Address for address;

    uint256 internal constant ERRCODE_OK = 0;

    ICERC20 public cToken;
    IComptroller public comptroller;
    address public rewards;
    ERC20 public stablecoin;

    constructor(
        address _cToken,
        address _comptroller,
        address _rewards,
        address _stablecoin
    ) public {
        // Verify input addresses
        require(
            _cToken != address(0) &&
                _comptroller != address(0) &&
                _rewards != address(0) &&
                _stablecoin != address(0),
            "CompoundERC20Market: An input address is 0"
        );
        require(
            _cToken.isContract() &&
                _comptroller.isContract() &&
                _rewards.isContract() &&
                _stablecoin.isContract(),
            "CompoundERC20Market: An input address is not a contract"
        );

        cToken = ICERC20(_cToken);
        comptroller = IComptroller(_comptroller);
        rewards = _rewards;
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external onlyOwner {
        require(amount > 0, "CompoundERC20Market: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Deposit `amount` stablecoin into cToken
        stablecoin.safeIncreaseAllowance(address(cToken), amount);
        require(
            cToken.mint(amount) == ERRCODE_OK,
            "CompoundERC20Market: Failed to mint cTokens"
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "CompoundERC20Market: amountInUnderlying is 0"
        );

        // Withdraw `amountInUnderlying` stablecoin from cToken
        require(
            cToken.redeemUnderlying(amountInUnderlying) == ERRCODE_OK,
            "CompoundERC20Market: Failed to redeem"
        );

        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        stablecoin.safeTransfer(msg.sender, amountInUnderlying);

        return amountInUnderlying;
    }

    function claimRewards() external {
        comptroller.claimComp(address(this));
        ERC20 comp = ERC20(comptroller.getCompAddress());
        comp.safeTransfer(rewards, comp.balanceOf(address(this)));
    }

    function totalValue() external returns (uint256) {
        uint256 cTokenBalance = cToken.balanceOf(address(this));
        // Amount of stablecoin units that 1 unit of cToken can be exchanged for, scaled by 10^18
        uint256 cTokenPrice = cToken.exchangeRateCurrent();
        return cTokenBalance.decmul(cTokenPrice);
    }

    function incomeIndex() external returns (uint256) {
        return cToken.exchangeRateCurrent();
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external onlyOwner {
        require(newValue.isContract(), "CompoundERC20Market: not contract");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }
}

File 47 of 64 : ICERC20.sol
pragma solidity 0.5.17;


// Compound finance ERC20 market interface
// Documentation: https://compound.finance/docs/ctokens
interface ICERC20 {
    function transfer(address dst, uint256 amount) external returns (bool);

    function transferFrom(address src, address dst, uint256 amount)
        external
        returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

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

    function balanceOfUnderlying(address owner) external returns (uint256);

    function getAccountSnapshot(address account)
        external
        view
        returns (uint256, uint256, uint256, uint256);

    function borrowRatePerBlock() external view returns (uint256);

    function supplyRatePerBlock() external view returns (uint256);

    function totalBorrowsCurrent() external returns (uint256);

    function borrowBalanceCurrent(address account) external returns (uint256);

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

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function getCash() external view returns (uint256);

    function accrueInterest() external returns (uint256);

    function seize(address liquidator, address borrower, uint256 seizeTokens)
        external
        returns (uint256);

    function mint(uint256 mintAmount) external returns (uint256);

    function redeem(uint256 redeemTokens) external returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

    function borrow(uint256 borrowAmount) external returns (uint256);

    function repayBorrow(uint256 repayAmount) external returns (uint256);

    function repayBorrowBehalf(address borrower, uint256 repayAmount)
        external
        returns (uint256);

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address cTokenCollateral
    ) external returns (uint256);
}

File 48 of 64 : IComptroller.sol
pragma solidity 0.5.17;


// Compound finance Comptroller interface
// Documentation: https://compound.finance/docs/comptroller
interface IComptroller {
    function claimComp(address holder) external;
    function getCompAddress() external view returns (address);
}

File 49 of 64 : HarvestMarket.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../IMoneyMarket.sol";
import "../../libs/DecMath.sol";
import "./imports/HarvestVault.sol";
import "./imports/HarvestStaking.sol";

contract HarvestMarket is IMoneyMarket, Ownable {
    using SafeMath for uint256;
    using DecMath for uint256;
    using SafeERC20 for ERC20;
    using Address for address;

    HarvestVault public vault;
    address public rewards;
    HarvestStaking public stakingPool;
    ERC20 public stablecoin;

    constructor(
        address _vault,
        address _rewards,
        address _stakingPool,
        address _stablecoin
    ) public {
        // Verify input addresses
        require(
            _vault != address(0) &&
                _rewards != address(0) &&
                _stakingPool != address(0) &&
                _stablecoin != address(0),
            "HarvestMarket: An input address is 0"
        );
        require(
            _vault.isContract() &&
                _rewards.isContract() &&
                _stakingPool.isContract() &&
                _stablecoin.isContract(),
            "HarvestMarket: An input address is not a contract"
        );

        vault = HarvestVault(_vault);
        rewards = _rewards;
        stakingPool = HarvestStaking(_stakingPool);
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external onlyOwner {
        require(amount > 0, "HarvestMarket: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to vault
        stablecoin.safeIncreaseAllowance(address(vault), amount);

        // Deposit `amount` stablecoin to vault
        vault.deposit(amount);

        // Stake vault token balance into staking pool
        uint256 vaultShareBalance = vault.balanceOf(address(this));
        vault.approve(address(stakingPool), vaultShareBalance);
        stakingPool.stake(vaultShareBalance);
    }

    function withdraw(uint256 amountInUnderlying)
        external
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "HarvestMarket: amountInUnderlying is 0"
        );

        // Withdraw `amountInShares` shares from vault
        uint256 sharePrice = vault.getPricePerFullShare();
        uint256 amountInShares = amountInUnderlying.decdiv(sharePrice);
        stakingPool.withdraw(amountInShares);
        vault.withdraw(amountInShares);

        // Transfer stablecoin to `msg.sender`
        actualAmountWithdrawn = stablecoin.balanceOf(address(this));
        stablecoin.safeTransfer(msg.sender, actualAmountWithdrawn);
    }

    function claimRewards() external {
        stakingPool.getReward();
        ERC20 rewardToken = ERC20(stakingPool.rewardToken());
        rewardToken.safeTransfer(rewards, rewardToken.balanceOf(address(this)));
    }

    function totalValue() external returns (uint256) {
        uint256 sharePrice = vault.getPricePerFullShare();
        uint256 shareBalance = vault.balanceOf(address(this)).add(stakingPool.balanceOf(address(this)));
        return shareBalance.decmul(sharePrice);
    }

    function incomeIndex() external returns (uint256) {
        return vault.getPricePerFullShare();
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external onlyOwner {
        require(newValue.isContract(), "HarvestMarket: not contract");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }
}

File 50 of 64 : HarvestVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.5.17;

interface HarvestVault {
    function deposit(uint256) external;

    function withdraw(uint256) external;

    function getPricePerFullShare() external view returns (uint256);

    function totalSupply() external view returns (uint256);

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

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

File 51 of 64 : HarvestStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.5.17;

interface HarvestStaking {
    function stake(uint256 amount) external;

    function withdraw(uint256 amount) external;

    function getReward() external;

    function rewardToken() external returns (address);

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

File 52 of 64 : Vault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.5.17;

interface Vault {
    function deposit(uint256) external;

    function withdraw(uint256) external;

    function getPricePerFullShare() external view returns (uint256);

    function totalSupply() external view returns (uint256);

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

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

File 53 of 64 : YVaultMarket.sol
pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../IMoneyMarket.sol";
import "../../libs/DecMath.sol";
import "./imports/Vault.sol";

contract YVaultMarket is IMoneyMarket, Ownable {
    using SafeMath for uint256;
    using DecMath for uint256;
    using SafeERC20 for ERC20;
    using Address for address;

    Vault public vault;
    ERC20 public stablecoin;

    constructor(address _vault, address _stablecoin) public {
        // Verify input addresses
        require(
            _vault != address(0) && _stablecoin != address(0),
            "YVaultMarket: An input address is 0"
        );
        require(
            _vault.isContract() && _stablecoin.isContract(),
            "YVaultMarket: An input address is not a contract"
        );

        vault = Vault(_vault);
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external onlyOwner {
        require(amount > 0, "YVaultMarket: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to vault
        stablecoin.safeIncreaseAllowance(address(vault), amount);

        // Deposit `amount` stablecoin to vault
        vault.deposit(amount);
    }

    function withdraw(uint256 amountInUnderlying)
        external
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "YVaultMarket: amountInUnderlying is 0"
        );

        // Withdraw `amountInShares` shares from vault
        uint256 sharePrice = vault.getPricePerFullShare();
        uint256 amountInShares = amountInUnderlying.decdiv(sharePrice);
        vault.withdraw(amountInShares);

        // Transfer stablecoin to `msg.sender`
        actualAmountWithdrawn = stablecoin.balanceOf(address(this));
        stablecoin.safeTransfer(msg.sender, actualAmountWithdrawn);
    }

    function claimRewards() external {}

    function totalValue() external returns (uint256) {
        uint256 sharePrice = vault.getPricePerFullShare();
        uint256 shareBalance = vault.balanceOf(address(this));
        return shareBalance.decmul(sharePrice);
    }

    function incomeIndex() external returns (uint256) {
        return vault.getPricePerFullShare();
    }

    function setRewards(address newValue) external {}
}

File 54 of 64 : Dumper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.5.17;

import "./OneSplitDumper.sol";
import "./withdrawers/CurveLPWithdrawer.sol";
import "./withdrawers/YearnWithdrawer.sol";

contract Dumper is OneSplitDumper, CurveLPWithdrawer, YearnWithdrawer {
    constructor(
        address _oneSplit,
        address _rewards,
        address _rewardToken
    ) public OneSplitDumper(_oneSplit, _rewards, _rewardToken) {}
}

File 55 of 64 : OneSplitDumper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.5.17;

import "@openzeppelin/contracts/access/roles/SignerRole.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./imports/OneSplitAudit.sol";
import "../IRewards.sol";

contract OneSplitDumper is SignerRole {
    using SafeERC20 for IERC20;

    OneSplitAudit public oneSplit;
    IRewards public rewards;
    IERC20 public rewardToken;

    constructor(
        address _oneSplit,
        address _rewards,
        address _rewardToken
    ) public {
        oneSplit = OneSplitAudit(_oneSplit);
        rewards = IRewards(_rewards);
        rewardToken = IERC20(_rewardToken);
    }

    function getDumpParams(address tokenAddress, uint256 parts)
        external
        view
        returns (uint256 returnAmount, uint256[] memory distribution)
    {
        IERC20 token = IERC20(tokenAddress);
        uint256 tokenBalance = token.balanceOf(address(this));
        (returnAmount, distribution) = oneSplit.getExpectedReturn(
            tokenAddress,
            address(rewardToken),
            tokenBalance,
            parts,
            0
        );
    }

    function dump(
        address tokenAddress,
        uint256 returnAmount,
        uint256[] calldata distribution
    ) external onlySigner {
        // dump token for rewardToken
        IERC20 token = IERC20(tokenAddress);
        uint256 tokenBalance = token.balanceOf(address(this));
        token.safeIncreaseAllowance(address(oneSplit), tokenBalance);

        uint256 rewardTokenBalanceBefore = rewardToken.balanceOf(address(this));
        oneSplit.swap(
            tokenAddress,
            address(rewardToken),
            tokenBalance,
            returnAmount,
            distribution,
            0
        );
        uint256 rewardTokenBalanceAfter = rewardToken.balanceOf(address(this));
        require(
            rewardTokenBalanceAfter > rewardTokenBalanceBefore,
            "OneSplitDumper: receivedRewardTokenAmount == 0"
        );
    }

    function notify() external onlySigner {
        uint256 balance = rewardToken.balanceOf(address(this));
        rewardToken.safeTransfer(address(rewards), balance);
        rewards.notifyRewardAmount(balance);
    }
}

File 56 of 64 : SignerRole.sol
pragma solidity ^0.5.0;

import "../../GSN/Context.sol";
import "../Roles.sol";

contract SignerRole is Context {
    using Roles for Roles.Role;

    event SignerAdded(address indexed account);
    event SignerRemoved(address indexed account);

    Roles.Role private _signers;

    constructor () internal {
        _addSigner(_msgSender());
    }

    modifier onlySigner() {
        require(isSigner(_msgSender()), "SignerRole: caller does not have the Signer role");
        _;
    }

    function isSigner(address account) public view returns (bool) {
        return _signers.has(account);
    }

    function addSigner(address account) public onlySigner {
        _addSigner(account);
    }

    function renounceSigner() public {
        _removeSigner(_msgSender());
    }

    function _addSigner(address account) internal {
        _signers.add(account);
        emit SignerAdded(account);
    }

    function _removeSigner(address account) internal {
        _signers.remove(account);
        emit SignerRemoved(account);
    }
}

File 57 of 64 : Roles.sol
pragma solidity ^0.5.0;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
    struct Role {
        mapping (address => bool) bearer;
    }

    /**
     * @dev Give an account access to this role.
     */
    function add(Role storage role, address account) internal {
        require(!has(role, account), "Roles: account already has role");
        role.bearer[account] = true;
    }

    /**
     * @dev Remove an account's access to this role.
     */
    function remove(Role storage role, address account) internal {
        require(has(role, account), "Roles: account does not have role");
        role.bearer[account] = false;
    }

    /**
     * @dev Check if an account has this role.
     * @return bool
     */
    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0), "Roles: account is the zero address");
        return role.bearer[account];
    }
}

File 58 of 64 : OneSplitAudit.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.5.17;

interface OneSplitAudit {
    function swap(
        address fromToken,
        address destToken,
        uint256 amount,
        uint256 minReturn,
        uint256[] calldata distribution,
        uint256 flags
    ) external payable;

    function getExpectedReturn(
        address fromToken,
        address destToken,
        uint256 amount,
        uint256 parts,
        uint256 flags // See constants in IOneSplit.sol
    )
        external
        view
        returns (uint256 returnAmount, uint256[] memory distribution);
}

File 59 of 64 : IRewards.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.5.17;

interface IRewards {
    function notifyRewardAmount(uint256 reward) external;
}

File 60 of 64 : CurveLPWithdrawer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.5.17;

import "@openzeppelin/contracts/access/roles/SignerRole.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../imports/Curve.sol";
import "../../IRewards.sol";

contract CurveLPWithdrawer is SignerRole {
    function curveWithdraw2(
        address lpTokenAddress,
        address curvePoolAddress,
        uint256[2] calldata minAmounts
    ) external onlySigner {
        IERC20 lpToken = IERC20(lpTokenAddress);
        uint256 lpTokenBalance = lpToken.balanceOf(address(this));
        ICurveFi curvePool = ICurveFi(curvePoolAddress);
        curvePool.remove_liquidity(lpTokenBalance, minAmounts);
    }

    function curveWithdraw3(
        address lpTokenAddress,
        address curvePoolAddress,
        uint256[3] calldata minAmounts
    ) external onlySigner {
        IERC20 lpToken = IERC20(lpTokenAddress);
        uint256 lpTokenBalance = lpToken.balanceOf(address(this));
        ICurveFi curvePool = ICurveFi(curvePoolAddress);
        curvePool.remove_liquidity(lpTokenBalance, minAmounts);
    }

    function curveWithdraw4(
        address lpTokenAddress,
        address curvePoolAddress,
        uint256[4] calldata minAmounts
    ) external onlySigner {
        IERC20 lpToken = IERC20(lpTokenAddress);
        uint256 lpTokenBalance = lpToken.balanceOf(address(this));
        ICurveFi curvePool = ICurveFi(curvePoolAddress);
        curvePool.remove_liquidity(lpTokenBalance, minAmounts);
    }

    function curveWithdraw5(
        address lpTokenAddress,
        address curvePoolAddress,
        uint256[5] calldata minAmounts
    ) external onlySigner {
        IERC20 lpToken = IERC20(lpTokenAddress);
        uint256 lpTokenBalance = lpToken.balanceOf(address(this));
        ICurveFi curvePool = ICurveFi(curvePoolAddress);
        curvePool.remove_liquidity(lpTokenBalance, minAmounts);
    }

    function curveWithdrawOneCoin(
        address lpTokenAddress,
        address curvePoolAddress,
        int128 coinIndex,
        uint256 minAmount
    ) external onlySigner {
        IERC20 lpToken = IERC20(lpTokenAddress);
        uint256 lpTokenBalance = lpToken.balanceOf(address(this));
        Zap curvePool = Zap(curvePoolAddress);
        curvePool.remove_liquidity_one_coin(
            lpTokenBalance,
            coinIndex,
            minAmount
        );
    }
}

File 61 of 64 : Curve.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.17;

interface ICurveFi {
    function remove_liquidity_imbalance(
        uint256[2] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[3] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[4] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity_imbalance(
        uint256[5] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity(uint256 _amount, uint256[2] calldata amounts)
        external;

    function remove_liquidity(uint256 _amount, uint256[3] calldata amounts)
        external;

    function remove_liquidity(uint256 _amount, uint256[4] calldata amounts)
        external;

    function remove_liquidity(uint256 _amount, uint256[5] calldata amounts)
        external;
}

interface Zap {
    function remove_liquidity_one_coin(
        uint256,
        int128,
        uint256
    ) external;
}

File 62 of 64 : YearnWithdrawer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.5.17;

import "@openzeppelin/contracts/access/roles/SignerRole.sol";
import "../imports/yERC20.sol";

contract YearnWithdrawer is SignerRole {
    function yearnWithdraw(address yTokenAddress) external onlySigner {
        yERC20 yToken = yERC20(yTokenAddress);
        uint256 balance = yToken.balanceOf(address(this));
        yToken.withdraw(balance);
    }
}

File 63 of 64 : yERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.5.17;

// NOTE: Basically an alias for Vaults
interface yERC20 {
    function balanceOf(address owner) external view returns (uint256);

    function deposit(uint256 _amount) external;

    function withdraw(uint256 _amount) external;

    function getPricePerFullShare() external view returns (uint256);
}

File 64 of 64 : Rewards.sol
/*
   ____            __   __        __   _
  / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
 _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
/___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
     /___/
* Synthetix: Rewards.sol
*
* Docs: https://docs.synthetix.io/
*
*
* MIT License
* ===========
*
* Copyright (c) 2020 Synthetix
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

pragma solidity 0.5.17;

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";

contract IRewardDistributionRecipient is Ownable {
    mapping(address => bool) public isRewardDistribution;

    function notifyRewardAmount(uint256 reward) external;

    modifier onlyRewardDistribution() {
        require(
            isRewardDistribution[_msgSender()],
            "Caller is not reward distribution"
        );
        _;
    }

    function setRewardDistribution(
        address _rewardDistribution,
        bool _isRewardDistribution
    ) external onlyOwner {
        isRewardDistribution[_rewardDistribution] = _isRewardDistribution;
    }
}

contract LPTokenWrapper {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    IERC20 public stakeToken;

    uint256 private _totalSupply;

    mapping(address => uint256) private _balances;

    constructor(address _stakeToken) public {
        stakeToken = IERC20(_stakeToken);
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function stake(uint256 amount) public {
        _totalSupply = _totalSupply.add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        stakeToken.safeTransferFrom(msg.sender, address(this), amount);
    }

    function withdraw(uint256 amount) public {
        _totalSupply = _totalSupply.sub(amount);
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        stakeToken.safeTransfer(msg.sender, amount);
    }
}

contract Rewards is LPTokenWrapper, IRewardDistributionRecipient {
    IERC20 public rewardToken;
    uint256 public constant DURATION = 7 days;

    uint256 public starttime;
    uint256 public periodFinish = 0;
    uint256 public rewardRate = 0;
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;

    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards;

    event RewardAdded(uint256 reward);
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);

    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    modifier checkStart {
        require(block.timestamp >= starttime, "Rewards: not start");
        _;
    }

    constructor(
        address _stakeToken,
        address _rewardToken,
        uint256 _starttime
    ) public LPTokenWrapper(_stakeToken) {
        rewardToken = IERC20(_rewardToken);
        starttime = _starttime;
    }

    function lastTimeRewardApplicable() public view returns (uint256) {
        return Math.min(block.timestamp, periodFinish);
    }

    function rewardPerToken() public view returns (uint256) {
        if (totalSupply() == 0) {
            return rewardPerTokenStored;
        }
        return
            rewardPerTokenStored.add(
                lastTimeRewardApplicable()
                    .sub(lastUpdateTime)
                    .mul(rewardRate)
                    .mul(1e18)
                    .div(totalSupply())
            );
    }

    function earned(address account) public view returns (uint256) {
        return
            balanceOf(account)
                .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                .div(1e18)
                .add(rewards[account]);
    }

    // stake visibility is public as overriding LPTokenWrapper's stake() function
    function stake(uint256 amount) public updateReward(msg.sender) checkStart {
        require(amount > 0, "Rewards: cannot stake 0");
        super.stake(amount);
        emit Staked(msg.sender, amount);
    }

    function withdraw(uint256 amount)
        public
        updateReward(msg.sender)
        checkStart
    {
        require(amount > 0, "Rewards: cannot withdraw 0");
        super.withdraw(amount);
        emit Withdrawn(msg.sender, amount);
    }

    function exit() external {
        withdraw(balanceOf(msg.sender));
        getReward();
    }

    function getReward() public updateReward(msg.sender) checkStart {
        uint256 reward = earned(msg.sender);
        if (reward > 0) {
            rewards[msg.sender] = 0;
            rewardToken.safeTransfer(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
        }
    }

    function notifyRewardAmount(uint256 reward)
        external
        onlyRewardDistribution
        updateReward(address(0))
    {
        // https://sips.synthetix.io/sips/sip-77
        require(reward > 0, "Rewards: reward == 0");
        require(
            reward < uint256(-1) / 10**18,
            "Rewards: rewards too large, would lock"
        );
        if (block.timestamp > starttime) {
            if (block.timestamp >= periodFinish) {
                rewardRate = reward.div(DURATION);
            } else {
                uint256 remaining = periodFinish.sub(block.timestamp);
                uint256 leftover = remaining.mul(rewardRate);
                rewardRate = reward.add(leftover).div(DURATION);
            }
            lastUpdateTime = block.timestamp;
            periodFinish = block.timestamp.add(DURATION);
            emit RewardAdded(reward);
        } else {
            rewardRate = reward.div(DURATION);
            lastUpdateTime = starttime;
            periodFinish = starttime.add(DURATION);
            emit RewardAdded(reward);
        }
    }
}

Settings
{
  "metadata": {},
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"uint256","name":"MinDepositPeriod","type":"uint256"},{"internalType":"uint256","name":"MaxDepositPeriod","type":"uint256"},{"internalType":"uint256","name":"MinDepositAmount","type":"uint256"},{"internalType":"uint256","name":"MaxDepositAmount","type":"uint256"}],"internalType":"struct DInterest.DepositLimit","name":"_depositLimit","type":"tuple"},{"internalType":"address","name":"_moneyMarket","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"},{"internalType":"address","name":"_feeModel","type":"address"},{"internalType":"address","name":"_interestModel","type":"address"},{"internalType":"address","name":"_interestOracle","type":"address"},{"internalType":"address","name":"_depositNFT","type":"address"},{"internalType":"address","name":"_fundingNFT","type":"address"},{"internalType":"address","name":"_mphMinter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maturationTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintMPHAmount","type":"uint256"}],"name":"EDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"fundingID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deficitAmount","type":"uint256"}],"name":"EFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"string","name":"paramName","type":"string"},{"indexed":false,"internalType":"address","name":"newValue","type":"address"}],"name":"ESetParamAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"string","name":"paramName","type":"string"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"ESetParamUint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"fundingID","type":"uint256"},{"indexed":false,"internalType":"bool","name":"early","type":"bool"},{"indexed":false,"internalType":"uint256","name":"takeBackMPHAmount","type":"uint256"}],"name":"EWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[],"name":"MaxDepositAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MaxDepositPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MinDepositAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MinDepositPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"uint256","name":"depositPeriodInSeconds","type":"uint256"}],"name":"calculateInterestAmount","outputs":[{"internalType":"uint256","name":"interestAmount","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maturationTimestamp","type":"uint256"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"depositIsFunded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"depositNFT","outputs":[{"internalType":"contract NFT","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"depositsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"depositID","type":"uint256"},{"internalType":"uint256","name":"fundingID","type":"uint256"}],"name":"earlyWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeModel","outputs":[{"internalType":"contract IFeeModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"fundAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"toDepositID","type":"uint256"}],"name":"fundMultiple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fundingListLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fundingNFT","outputs":[{"internalType":"contract NFT","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"depositID","type":"uint256"}],"name":"getDeposit","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maturationTimestamp","type":"uint256"},{"internalType":"uint256","name":"interestOwed","type":"uint256"},{"internalType":"uint256","name":"initialMoneyMarketIncomeIndex","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"finalSurplusIsNegative","type":"bool"},{"internalType":"uint256","name":"finalSurplusAmount","type":"uint256"},{"internalType":"uint256","name":"mintMPHAmount","type":"uint256"},{"internalType":"uint256","name":"depositTimestamp","type":"uint256"}],"internalType":"struct DInterest.Deposit","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"fundingID","type":"uint256"}],"name":"getFunding","outputs":[{"components":[{"internalType":"uint256","name":"fromDepositID","type":"uint256"},{"internalType":"uint256","name":"toDepositID","type":"uint256"},{"internalType":"uint256","name":"recordedFundedDepositAmount","type":"uint256"},{"internalType":"uint256","name":"recordedMoneyMarketIncomeIndex","type":"uint256"},{"internalType":"uint256","name":"creationTimestamp","type":"uint256"}],"internalType":"struct DInterest.Funding","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"interestModel","outputs":[{"internalType":"contract IInterestModel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"interestOracle","outputs":[{"internalType":"contract IInterestOracle","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"latestFundedDepositID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"moneyMarket","outputs":[{"internalType":"contract IMoneyMarket","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"moneyMarketIncomeIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"mphMinter","outputs":[{"internalType":"contract MPHMinter","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"amountList","type":"uint256[]"},{"internalType":"uint256[]","name":"maturationTimestampList","type":"uint256[]"}],"name":"multiDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"depositIDList","type":"uint256[]"},{"internalType":"uint256[]","name":"fundingIDList","type":"uint256[]"}],"name":"multiEarlyWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"depositIDList","type":"uint256[]"},{"internalType":"uint256[]","name":"fundingIDList","type":"uint256[]"}],"name":"multiWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setDepositNFTBaseURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setDepositNFTContractURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"newURI","type":"string"}],"name":"setDepositNFTTokenURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setFeeModel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setFundingNFTBaseURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setFundingNFTContractURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"newURI","type":"string"}],"name":"setFundingNFTTokenURI","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setInterestModel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setInterestOracle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setMPHMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setMaxDepositAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setMaxDepositPeriod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setMinDepositAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setMinDepositPeriod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newValue","type":"address"}],"name":"setRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"stablecoin","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"surplus","outputs":[{"internalType":"bool","name":"isNegative","type":"bool"},{"internalType":"uint256","name":"surplusAmount","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"depositID","type":"uint256"}],"name":"surplusOfDeposit","outputs":[{"internalType":"bool","name":"isNegative","type":"bool"},{"internalType":"uint256","name":"surplusAmount","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalInterestOwed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"unfundedUserDepositAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"depositID","type":"uint256"},{"internalType":"uint256","name":"fundingID","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061030c5760003560e01c8063a5a9504e1161019d578063cf8bb3e8116100e9578063e9cbd822116100a2578063f154240a1161007c578063f154240a14610616578063f20b1c761461061e578063f2fde38b14610631578063f6153ccd146106445761030c565b8063e9cbd822146105db578063ebed4bd4146105e3578063ec38a862146106035761030c565b8063cf8bb3e81461057f578063d2402b1914610592578063d6d75f511461059a578063d816bd7f146105a2578063e2bbb158146105b5578063e3d075df146105c85761030c565b8063b817b51e11610156578063c016cfd611610130578063c016cfd61461053e578063c07b767d14610551578063c4511c6a14610559578063ca538ada1461056c5761030c565b8063b817b51e1461051b578063bb59f11a14610523578063bc135d751461052b5761030c565b8063a5a9504e146104ca578063a832806b146104dd578063ac165d7a146104e5578063aea62e3f146104ed578063af13a23e14610500578063b1710a62146105135761030c565b8063619c5cb71161025c5780638f168c9311610215578063956087c1116101ef578063956087c11461047c57806397ee11441461048f5780639f9fb96814610497578063a5783afa146104b75761030c565b80638f168c93146104575780638f32d59b1461045f578063939156a2146104745761030c565b8063619c5cb7146103f957806367e3c4d41461040c5780636e3f5d5214610414578063715018a6146104275780637aebdce61461042f5780638da5cb5b146104425761030c565b806321eede69116102c95780632fc082ff116102a35780632fc082ff146103ab578063441a3e70146103c057806351521805146103d35780635fef1465146103e65761030c565b806321eede6914610388578063259fc70f146103905780632a80cda3146103985761030c565b806301f2249b14610311578063029eeaa814610326578063037b6b0d1461033957806308b856d714610357578063138885651461035f5780632125b98414610375575b600080fd5b61032461031f366004612fca565b61064c565b005b610324610334366004613110565b61071a565b610341610a92565b60405161034e9190613fe1565b60405180910390f35b610341610a98565b610367610a9e565b60405161034e929190613dc5565b6103246103833660046130ce565b610b87565b610341610c13565b610341610c19565b6103246103a6366004613110565b610c1f565b6103b3610cb7565b60405161034e9190613dd3565b6103246103ce3660046131a2565b610cc6565b6103246103e1366004613006565b610d10565b6103246103f4366004613110565b610dc2565b6103246104073660046130ce565b610e19565b6103b3610e6f565b610324610422366004612fca565b610e7e565b610324610ef7565b61032461043d3660046131a2565b610f6a565b61044a610fa2565b60405161034e9190613c7d565b610341610fb6565b610467610fbc565b60405161034e9190613db7565b6103b3610fe5565b61032461048a36600461314c565b610ff4565b6103b3611083565b6104aa6104a5366004613110565b611092565b60405161034e9190613fc4565b6103676104c5366004613110565b61113f565b6103246104d8366004612fca565b611268565b6103b36112e1565b6103b36112f0565b6103246104fb366004613006565b6112ff565b61046761050e366004613110565b61139d565b6103246113a5565b6103416115ed565b610341611670565b6103416105393660046131a2565b611676565b61032461054c3660046130ce565b61179a565b6103416117f0565b610324610567366004613110565b6117f6565b61032461057a366004613110565b611859565b61032461058d366004613006565b6118bc565b610341611958565b6103b361195e565b6103246105b036600461314c565b61196d565b6103246105c33660046131a2565b6119c5565b6103246105d63660046130ce565b6119fb565b6103b3611a51565b6105f66105f1366004613110565b611a60565b60405161034e9190613fd3565b610324610611366004612fca565b611ad7565b610341611b97565b61032461062c366004612fca565b611b9d565b61032461063f366004612fca565b611c16565b610341611c46565b610654610fbc565b6106795760405162461bcd60e51b815260040161067090613f14565b60405180910390fd5b61068b816001600160a01b0316611c4c565b6106a75760405162461bcd60e51b815260040161067090613f74565b601280546001600160a01b0319166001600160a01b0383161790556040516106ce90613c3b565b6040518091039020336001600160a01b03167f64b03eb8356730cffd396927eec0e9b1e0599498960e022df3dae35791c17cf58360405161070f9190613c7d565b60405180910390a350565b60005460ff1661073c5760405162461bcd60e51b815260040161067090613f94565b6000805460ff1916905560025481116107675760405162461bcd60e51b815260040161067090613f54565b6001548111156107895760405162461bcd60e51b815260040161067090613e44565b600080610794610a9e565b91509150816107b55760405162461bcd60e51b815260040161067090613f24565b6002546000908190819081906107d290600163ffffffff611c8816565b90505b86811161088e5760006107e782611cb4565b600481015490915060ff161561080a576108008261113f565b9097509550610822565b6004810154600582015461010090910460ff16975095505b861561083f57610838858763ffffffff611c8816565b9450610852565b61084f848763ffffffff611c8816565b93505b600481015460ff161561087557805461087290849063ffffffff611c8816565b92505b5061088781600163ffffffff611c8816565b90506107d5565b508282106108ae5760405162461bcd60e51b815260040161067090613e14565b6108be838363ffffffff611ce716565b9250600b546040805163010e130960e51b815290516000926001600160a01b0316916321c2612091600480830192602092919082900301818787803b15801561090657600080fd5b505af115801561091a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061093e919081019061312e565b9050600081116109605760405162461bcd60e51b815260040161067090613f34565b6040805160a081018252600280548252602082018a81529282018581526060830185815242608085019081526004805460018101825560009190915294517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b60059096029586015594517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c85015590517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d840155517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e83015591517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19f90910155879055600354610a709083611ce7565b600355610a7c84611d29565b50506000805460ff191660011790555050505050565b60085481565b60045490565b6000806000600b60009054906101000a90046001600160a01b03166001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610af357600080fd5b505af1158015610b07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b2b919081019061312e565b90506000610b46600a54600954611c8890919063ffffffff16565b9050808210610b6a5760009350610b63828263ffffffff611ce716565b9250610b81565b60019350610b7e818363ffffffff611ce716565b92505b50509091565b610b8f610fbc565b610bab5760405162461bcd60e51b815260040161067090613f14565b6010546040516355f804b360e01b81526001600160a01b03909116906355f804b390610bdd9085908590600401613de1565b600060405180830381600087803b158015610bf757600080fd5b505af1158015610c0b573d6000803e3d6000fd5b505050505050565b60025481565b60065481565b610c27610fbc565b610c435760405162461bcd60e51b815260040161067090613f14565b600854811115610c655760405162461bcd60e51b815260040161067090613e24565b6007819055604051610c7690613c51565b6040518091039020336001600160a01b03167ff37f82a82443ce2d0a9a47ee78cef1a46975e3f33782fcb8caf315626b73a3d48360405161070f9190613fe1565b600f546001600160a01b031681565b60005460ff16610ce85760405162461bcd60e51b815260040161067090613f94565b6000805460ff19168155610cff9083908390611e75565b50506000805460ff19166001179055565b60005460ff16610d325760405162461bcd60e51b815260040161067090613f94565b6000805460ff19169055828114610d5b5760405162461bcd60e51b815260040161067090613e74565b60005b83811015610dae57610d96858583818110610d7557fe5b90506020020135848484818110610d8857fe5b905060200201356001611e75565b610da781600163ffffffff611c8816565b9050610d5e565b50506000805460ff19166001179055505050565b610dca610fbc565b610de65760405162461bcd60e51b815260040161067090613f14565b600654811115610e085760405162461bcd60e51b815260040161067090613e24565b6005819055604051610c7690613c5c565b610e21610fbc565b610e3d5760405162461bcd60e51b815260040161067090613f14565b60115460405163938e3d7b60e01b81526001600160a01b039091169063938e3d7b90610bdd9085908590600401613de1565b6010546001600160a01b031681565b610e86610fbc565b610ea25760405162461bcd60e51b815260040161067090613f14565b610eb4816001600160a01b0316611c4c565b610ed05760405162461bcd60e51b815260040161067090613f74565b600e80546001600160a01b0319166001600160a01b0383161790556040516106ce90613c30565b610eff610fbc565b610f1b5760405162461bcd60e51b815260040161067090613f14565b600080546040516101009091046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a360008054610100600160a81b0319169055565b60005460ff16610f8c5760405162461bcd60e51b815260040161067090613f94565b6000805460ff19169055610cff82826001611e75565b60005461010090046001600160a01b031690565b600a5481565b6000805461010090046001600160a01b0316610fd6612344565b6001600160a01b031614905090565b6011546001600160a01b031681565b610ffc610fbc565b6110185760405162461bcd60e51b815260040161067090613f14565b601154604051630588253160e21b81526001600160a01b039091169063162094c49061104c90869086908690600401613fef565b600060405180830381600087803b15801561106657600080fd5b505af115801561107a573d6000803e3d6000fd5b50505050505050565b600d546001600160a01b031681565b61109a612e89565b60016110ac838263ffffffff611ce716565b815481106110b657fe5b6000918252602091829020604080516101208101825260089093029091018054835260018101549383019390935260028301549082015260038201546060820152600482015460ff808216151560808401526101009182900416151560a0830152600583015460c0830152600683015460e0830152600790920154918101919091529050919050565b600080600061114d84611cb4565b90506000600b60009054906101000a90046001600160a01b03166001600160a01b03166321c261206040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111a157600080fd5b505af11580156111b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111d9919081019061312e565b9050600061120883600301546111fc84866000015461234890919063ffffffff16565b9063ffffffff61238216565b600284015484549192506000916112249163ffffffff611c8816565b90508082106112485760009550611241828263ffffffff611ce716565b945061125f565b6001955061125c818363ffffffff611ce716565b94505b50505050915091565b611270610fbc565b61128c5760405162461bcd60e51b815260040161067090613f14565b61129e816001600160a01b0316611c4c565b6112ba5760405162461bcd60e51b815260040161067090613f74565b600d80546001600160a01b0319166001600160a01b0383161790556040516106ce90613c1a565b6012546001600160a01b031681565b600e546001600160a01b031681565b60005460ff166113215760405162461bcd60e51b815260040161067090613f94565b6000805460ff1916905582811461134a5760405162461bcd60e51b815260040161067090613e74565b60005b83811015610dae5761138585858381811061136457fe5b9050602002013584848481811061137757fe5b905060200201356000611e75565b61139681600163ffffffff611c8816565b905061134d565b600254101590565b60005460ff166113c75760405162461bcd60e51b815260040161067090613f94565b6000805460ff19168155806113da610a9e565b91509150816113fb5760405162461bcd60e51b815260040161067090613f24565b6001546114079061139d565b156114245760405162461bcd60e51b815260040161067090613e04565b600b546040805163010e130960e51b815290516000926001600160a01b0316916321c2612091600480830192602092919082900301818787803b15801561146a57600080fd5b505af115801561147e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114a2919081019061312e565b9050600081116114c45760405162461bcd60e51b815260040161067090613f34565b6040805160a081018252600280548252600180546020840190815260038054958501958652606085018781524260808701908152600480548087018255600091825297517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b60059099029889015593517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c88015596517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d870155517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e86015594517f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19f9094019390935554905590556115db82611d29565b50506000805460ff1916600117905550565b600b546040805163010e130960e51b815290516000926001600160a01b0316916321c2612091600480830192602092919082900301818787803b15801561163357600080fd5b505af1158015611647573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061166b919081019061312e565b905090565b60035481565b600f54604080516385b3a93160e01b8152815160009384936001600160a01b03909116926385b3a931926004808301939282900301818787803b1580156116bc57600080fd5b505af11580156116d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116f49190810190613094565b915050600080611702610a9e565b600e5460405163684df1a760e11b81529294509092506001600160a01b03169063d09be34e9061173e9089908990889088908890600401614010565b60206040518083038186803b15801561175657600080fd5b505afa15801561176a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061178e919081019061312e565b93505050505b92915050565b6117a2610fbc565b6117be5760405162461bcd60e51b815260040161067090613f14565b60105460405163938e3d7b60e01b81526001600160a01b039091169063938e3d7b90610bdd9085908590600401613de1565b60055481565b6117fe610fbc565b61181a5760405162461bcd60e51b815260040161067090613f14565b600754811015801561182c5750600081115b6118485760405162461bcd60e51b815260040161067090613e24565b6008819055604051610c7690613c67565b611861610fbc565b61187d5760405162461bcd60e51b815260040161067090613f14565b600554811015801561188f5750600081115b6118ab5760405162461bcd60e51b815260040161067090613e24565b6006819055604051610c7690613c46565b60005460ff166118de5760405162461bcd60e51b815260040161067090613f94565b6000805460ff191690558281146119075760405162461bcd60e51b815260040161067090613e74565b60005b83811015610dae5761194085858381811061192157fe5b9050602002013584848481811061193457fe5b905060200201356123c4565b61195181600163ffffffff611c8816565b905061190a565b60075481565b600b546001600160a01b031681565b611975610fbc565b6119915760405162461bcd60e51b815260040161067090613f14565b601054604051630588253160e21b81526001600160a01b039091169063162094c49061104c90869086908690600401613fef565b60005460ff166119e75760405162461bcd60e51b815260040161067090613f94565b6000805460ff19169055610cff82826123c4565b611a03610fbc565b611a1f5760405162461bcd60e51b815260040161067090613f14565b6011546040516355f804b360e01b81526001600160a01b03909116906355f804b390610bdd9085908590600401613de1565b600c546001600160a01b031681565b611a68612ed9565b6004611a7b83600163ffffffff611ce716565b81548110611a8557fe5b90600052602060002090600502016040518060a0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815250509050919050565b611adf610fbc565b611afb5760405162461bcd60e51b815260040161067090613f14565b611b0d816001600160a01b0316611c4c565b611b295760405162461bcd60e51b815260040161067090613f74565b600b5460405163761c543160e11b81526001600160a01b039091169063ec38a86290611b59908490600401613c7d565b600060405180830381600087803b158015611b7357600080fd5b505af1158015611b87573d6000803e3d6000fd5b505050506040516106ce90613c25565b60015490565b611ba5610fbc565b611bc15760405162461bcd60e51b815260040161067090613f14565b611bd3816001600160a01b0316611c4c565b611bef5760405162461bcd60e51b815260040161067090613f74565b600f80546001600160a01b0319166001600160a01b0383161790556040516106ce90613c72565b611c1e610fbc565b611c3a5760405162461bcd60e51b815260040161067090613f14565b611c438161280e565b50565b60095481565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611c8057508115155b949350505050565b600082820183811015611cad5760405162461bcd60e51b815260040161067090613e64565b9392505050565b60006001611cc8838263ffffffff611ce716565b81548110611cd257fe5b90600052602060002090600802019050919050565b6000611cad83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061289a565b600c54611d47906001600160a01b031633308463ffffffff6128c616565b600b54600c54611d6a916001600160a01b0391821691168363ffffffff61292716565b600b5460405163b6b55f2560e01b81526001600160a01b039091169063b6b55f2590611d9a908490600401613fe1565b600060405180830381600087803b158015611db457600080fd5b505af1158015611dc8573d6000803e3d6000fd5b5050601154600480546040516340c10f1960e01b81526001600160a01b0390931694506340c10f199350611dfe92339201613c8b565b600060405180830381600087803b158015611e1857600080fd5b505af1158015611e2c573d6000803e3d6000fd5b505060045460405190925082915033907fb03542817f50f5c2413ab7023425536491098da0d40edaf6b3daeb10f69d922e90611e69908690613fe1565b60405180910390a35050565b6000611e8084611cb4565b600481015490915060ff16611ea75760405162461bcd60e51b815260040161067090613ea4565b60048101805460ff191690558115611f045780600101544210611edc5760405162461bcd60e51b815260040161067090613f64565b80600701544211611eff5760405162461bcd60e51b815260040161067090613fa4565b611f28565b8060010154421015611f285760405162461bcd60e51b815260040161067090613ec4565b6010546040516331a9108f60e11b815233916001600160a01b031690636352211e90611f58908890600401613fe1565b60206040518083038186803b158015611f7057600080fd5b505afa158015611f84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611fa89190810190612fe8565b6001600160a01b031614611fce5760405162461bcd60e51b815260040161067090613f44565b60125460068201546040516319717aa560e21b81526000926001600160a01b0316916365c5ea94916120069133918890600401613ca6565b602060405180830381600087803b15801561202057600080fd5b505af1158015612034573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612058919081019061312e565b8254600954919250612070919063ffffffff611ce716565b6009556002820154600a5461208a9163ffffffff611ce716565b600a55600080841561209e5750825461213e565b600d546002850154604051633f3b917d60e21b81526001600160a01b039092169163fcee45f4916120d191600401613fe1565b60206040518083038186803b1580156120e957600080fd5b505afa1580156120fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612121919081019061312e565b6002850154855491935061213b919063ffffffff611c8816565b90505b600b54604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d9061216e908490600401613fe1565b602060405180830381600087803b15801561218857600080fd5b505af115801561219c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506121c0919081019061312e565b90506000806121ce8961113f565b915091506121db8961139d565b156121fc576121f7888a8860000154896001015486868d6129dc565b612230565b85546003546122109163ffffffff611ce716565b60035560048601805461ff00191661010084151502179055600586018190555b61225d33612244858763ffffffff611ce716565b600c546001600160a01b0316919063ffffffff612d1216565b600d54604080516338af3eed60e01b815290516122f4926001600160a01b0316916338af3eed916004808301926020929190829003018186803b1580156122a357600080fd5b505afa1580156122b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122db9190810190612fe8565b600c546001600160a01b0316908663ffffffff612d1216565b8789336001600160a01b03167ffa1c3f2020dd155614d78b1648966f6b1079921cb29a52e26eb48b67c8cae7cd8a89604051612331929190613dc5565b60405180910390a4505050505050505050565b3390565b60008261235757506000611794565b8282028284828161236457fe5b0414611cad5760405162461bcd60e51b815260040161067090613f04565b6000611cad83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612d39565b600082116123e45760405162461bcd60e51b815260040161067090613ef4565b60075482101580156123f857506008548211155b6124145760405162461bcd60e51b815260040161067090613e94565b6000612426824263ffffffff611ce716565b9050600554811015801561243c57506006548111155b6124585760405162461bcd60e51b815260040161067090613ed4565b60095461246b908463ffffffff611c8816565b60095560018054600091612485919063ffffffff611c8816565b60035490915061249b908563ffffffff611c8816565b60035560006124aa8584611676565b9050600081116124cc5760405162461bcd60e51b815260040161067090613ee4565b600a546124df908263ffffffff611c8816565b600a55601254604051635b4c5e4360e11b81526000916001600160a01b03169063b698bc86906125199033908a9089908890600401613cce565b602060405180830381600087803b15801561253357600080fd5b505af1158015612547573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061256b919081019061312e565b90506001604051806101200160405280888152602001878152602001848152602001600b60009054906101000a90046001600160a01b03166001600160a01b03166321c261206040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156125dd57600080fd5b505af11580156125f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612615919081019061312e565b81526001602080830182905260006040808501829052606080860183905260808087018a90524260a09788015288548087018a55988452928490208751600890990201978855928601519387019390935591840151600286015583015160038501558201516004840180549284015160ff199093169115159190911761ff001916610100921515830217905560c0820151600584015560e082015160068401550151600790910155600c546126d5906001600160a01b03163330896128c6565b600b54600c546126f8916001600160a01b0391821691168863ffffffff61292716565b600b5460405163b6b55f2560e01b81526001600160a01b039091169063b6b55f2590612728908990600401613fe1565b600060405180830381600087803b15801561274257600080fd5b505af1158015612756573d6000803e3d6000fd5b50506010546040516340c10f1960e01b81526001600160a01b0390911692506340c10f19915061278c9033908790600401613c8b565b600060405180830381600087803b1580156127a657600080fd5b505af11580156127ba573d6000803e3d6000fd5b5050505082336001600160a01b03167f732ec50d46689f84d1ff0eba004a7affafc5460739dc8780d4d44c81611ace40888886866040516127fe949392919061405c565b60405180910390a3505050505050565b6001600160a01b0381166128345760405162461bcd60e51b815260040161067090613e34565b600080546040516001600160a01b038085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600081848411156128be5760405162461bcd60e51b81526004016106709190613df3565b505050900390565b6040516129219085906323b872dd60e01b906128ea90879087908790602401613d27565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612d70565b50505050565b60006129b782856001600160a01b031663dd62ed3e30876040518363ffffffff1660e01b815260040161295b929190613d0c565b60206040518083038186803b15801561297357600080fd5b505afa158015612987573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129ab919081019061312e565b9063ffffffff611c8816565b60405190915061292190859063095ea7b360e01b906128ea9087908690602401613d4f565b60006129e788612e55565b8054909150871180156129fe575080600101548711155b612a1a5760405162461bcd60e51b815260040161067090613eb4565b600b546040805163010e130960e51b815290516000926001600160a01b0316916321c2612091600480830192602092919082900301818787803b158015612a6057600080fd5b505af1158015612a74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a98919081019061312e565b905060008111612aba5760405162461bcd60e51b815260040161067090613e54565b60028201546003830154600091612aeb91612adf906111fc838763ffffffff61234816565b9063ffffffff611ce716565b6002840154909150612b03908963ffffffff611ce716565b6002840155600383018290556011546040516331a9108f60e11b81526000916001600160a01b031690636352211e90612b40908e90600401613fe1565b60206040518083038186803b158015612b5857600080fd5b505afa158015612b6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b909190810190612fe8565b90506000858015612b9e5750875b612ba85782612bb8565b612bb8838863ffffffff611c8816565b90508015612c6257600b54604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90612bf0908490600401613fe1565b602060405180830381600087803b158015612c0a57600080fd5b505af1158015612c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c42919081019061312e565b600c54909150612c62906001600160a01b0316838363ffffffff612d1216565b601260009054906101000a90046001600160a01b03166001600160a01b03166326d70211838c88600401548d888c6040518763ffffffff1660e01b8152600401612cb196959493929190613d5d565b602060405180830381600087803b158015612ccb57600080fd5b505af1158015612cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d03919081019061312e565b50505050505050505050505050565b604051612d3490849063a9059cbb60e01b906128ea9086908690602401613d4f565b505050565b60008183612d5a5760405162461bcd60e51b81526004016106709190613df3565b506000838581612d6657fe5b0495945050505050565b612d82826001600160a01b0316611c4c565b612d9e5760405162461bcd60e51b815260040161067090613fb4565b60006060836001600160a01b031683604051612dba9190613c0e565b6000604051808303816000865af19150503d8060008114612df7576040519150601f19603f3d011682016040523d82523d6000602084013e612dfc565b606091505b509150915081612e1e5760405162461bcd60e51b815260040161067090613e84565b8051156129215780806020019051612e399190810190613076565b6129215760405162461bcd60e51b815260040161067090613f84565b60006004612e6a83600163ffffffff611ce716565b81548110612e7457fe5b90600052602060002090600502019050919050565b604051806101200160405280600081526020016000815260200160008152602001600081526020016000151581526020016000151581526020016000815260200160008152602001600081525090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8035611794816140ef565b8051611794816140ef565b60008083601f840112612f3057600080fd5b50813567ffffffffffffffff811115612f4857600080fd5b602083019150836020820283011115612f6057600080fd5b9250929050565b805161179481614103565b60008083601f840112612f8457600080fd5b50813567ffffffffffffffff811115612f9c57600080fd5b602083019150836001820283011115612f6057600080fd5b80356117948161410c565b80516117948161410c565b600060208284031215612fdc57600080fd5b6000611c808484612f08565b600060208284031215612ffa57600080fd5b6000611c808484612f13565b6000806000806040858703121561301c57600080fd5b843567ffffffffffffffff81111561303357600080fd5b61303f87828801612f1e565b9450945050602085013567ffffffffffffffff81111561305e57600080fd5b61306a87828801612f1e565b95989497509550505050565b60006020828403121561308857600080fd5b6000611c808484612f67565b600080604083850312156130a757600080fd5b60006130b38585612f67565b92505060206130c485828601612fbf565b9150509250929050565b600080602083850312156130e157600080fd5b823567ffffffffffffffff8111156130f857600080fd5b61310485828601612f72565b92509250509250929050565b60006020828403121561312257600080fd5b6000611c808484612fb4565b60006020828403121561314057600080fd5b6000611c808484612fbf565b60008060006040848603121561316157600080fd5b600061316d8686612fb4565b935050602084013567ffffffffffffffff81111561318a57600080fd5b61319686828701612f72565b92509250509250925092565b600080604083850312156131b557600080fd5b60006131c18585612fb4565b92505060206130c485828601612fb4565b6131db8161409b565b82525050565b6131db8161407c565b6131db81614087565b60006131fe8261406a565b613208818561406e565b93506132188185602086016140b9565b9290920192915050565b6131db816140a2565b60006132378385614073565b93506132448385846140ad565b61324d836140e5565b9093019392505050565b60006132628261406a565b61326c8185614073565b935061327c8185602086016140b9565b61324d816140e5565b6000613292601e83614073565b7f44496e7465726573743a20416c6c206465706f736974732066756e6465640000815260200192915050565b60006132cb602783614073565b7f44496e7465726573743a2053656c6563746564206465706f7369747320696e20815266737572706c757360c81b602082015260400192915050565b6000613314601883614073565b7f44496e7465726573743a20696e76616c69642076616c75650000000000000000815260200192915050565b600061334d602683614073565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000613395601e83614073565b7f44496e7465726573743a20496e76616c696420746f4465706f73697449440000815260200192915050565b60006133ce602d83614073565b7f44496e7465726573743a2063757272656e744d6f6e65794d61726b6574496e6381526c06f6d65496e646578203d3d203609c1b602082015260400192915050565b600061341d601b83614073565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000613456601f83614073565b7f44496e7465726573743a204c697374206c656e6774687320756e657175616c00815260200192915050565b600061348f60088361406e565b67199959535bd9195b60c21b815260080192915050565b60006134b360138361406e565b726d6f6e65794d61726b65742e7265776172647360681b815260130192915050565b60006134e2602083614073565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b600061351b602683614073565b7f44496e7465726573743a204465706f73697420616d6f756e74206f7574206f668152652072616e676560d01b602082015260400192915050565b6000613563600d8361406e565b6c1a5b9d195c995cdd135bd9195b609a1b8152600d0192915050565b600061358c601d83614073565b7f44496e7465726573743a204465706f736974206e6f7420616374697665000000815260200192915050565b60006135c5602a83614073565b7f44496e7465726573743a204465706f736974206e6f742066756e64656420627981526908199d5b991a5b99d25160b21b602082015260400192915050565b6000613611601d83614073565b7f44496e7465726573743a204465706f736974206e6f74206d6174757265000000815260200192915050565b600061364a602683614073565b7f44496e7465726573743a204465706f73697420706572696f64206f7574206f668152652072616e676560d01b602082015260400192915050565b600061369260098361406e565b6836b83426b4b73a32b960b91b815260090192915050565b60006136b7601e83614073565b7f44496e7465726573743a20696e746572657374416d6f756e74203d3d20300000815260200192915050565b60006136f0601e83614073565b7f44496e7465726573743a204465706f73697420616d6f756e7420697320300000815260200192915050565b6000613729602183614073565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061376c602083614073565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b60006137a5601f83614073565b7f44496e7465726573743a204e6f206465666963697420617661696c61626c6500815260200192915050565b60006137de601b83614073565b7f44496e7465726573743a20696e636f6d65496e646578203d3d20300000000000815260200192915050565b6000613817602883614073565b7f44496e7465726573743a2053656e64657220646f65736e2774206f776e2064658152671c1bdcda5d13919560c21b602082015260400192915050565b600061386160108361406e565b6f13585e11195c1bdcda5d14195c9a5bd960821b815260100192915050565b600061388d60108361406e565b6f135a5b91195c1bdcda5d105b5bdd5b9d60821b815260100192915050565b60006138b960108361406e565b6f135a5b91195c1bdcda5d14195c9a5bd960821b815260100192915050565b60006138e5602283614073565b7f44496e7465726573743a204465706f7369747320616c72656164792066756e64815261195960f21b602082015260400192915050565b6000613929603183614073565b7f44496e7465726573743a204465706f736974206d61747572652c2075736520778152701a5d1a191c985dca0a481a5b9cdd195859607a1b602082015260400192915050565b600061397c601783614073565b7f44496e7465726573743a206e6f7420636f6e7472616374000000000000000000815260200192915050565b60006139b560108361406e565b6f13585e11195c1bdcda5d105b5bdd5b9d60821b815260100192915050565b60006139e1602a83614073565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6000613a2d600e8361406e565b6d696e7465726573744f7261636c6560901b8152600e0192915050565b6000613a57601f83614073565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b6000613a90602283614073565b7f44496e7465726573743a204465706f736974656420696e2073616d6520626c6f815261636b60f01b602082015260400192915050565b6000613ad4601f83614073565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b8051610120830190613b128482613c05565b506020820151613b256020850182613c05565b506040820151613b386040850182613c05565b506060820151613b4b6060850182613c05565b506080820151613b5e60808501826131ea565b5060a0820151613b7160a08501826131ea565b5060c0820151613b8460c0850182613c05565b5060e0820151613b9760e0850182613c05565b50610100820151612921610100850182613c05565b805160a0830190613bbd8482613c05565b506020820151613bd06020850182613c05565b506040820151613be36040850182613c05565b506060820151613bf66060850182613c05565b50608082015161292160808501825b6131db81614098565b6000611cad82846131f3565b600061179482613482565b6000611794826134a6565b600061179482613556565b600061179482613685565b600061179482613854565b600061179482613880565b6000611794826138ac565b6000611794826139a8565b600061179482613a20565b6020810161179482846131e1565b60408101613c9982856131d2565b611cad6020830184613c05565b60608101613cb482866131d2565b613cc16020830185613c05565b611c8060408301846131ea565b60808101613cdc82876131d2565b613ce96020830186613c05565b613cf66040830185613c05565b613d036060830184613c05565b95945050505050565b60408101613d1a82856131e1565b611cad60208301846131e1565b60608101613d3582866131e1565b613d4260208301856131e1565b611c806040830184613c05565b60408101613c9982856131e1565b60c08101613d6b82896131e1565b613d786020830188613c05565b613d856040830187613c05565b613d926060830186613c05565b613d9f6080830185613c05565b613dac60a08301846131ea565b979650505050505050565b6020810161179482846131ea565b60408101613c9982856131ea565b602081016117948284613222565b60208082528101611c8081848661322b565b60208082528101611cad8184613257565b6020808252810161179481613285565b60208082528101611794816132be565b6020808252810161179481613307565b6020808252810161179481613340565b6020808252810161179481613388565b60208082528101611794816133c1565b6020808252810161179481613410565b6020808252810161179481613449565b60208082528101611794816134d5565b602080825281016117948161350e565b602080825281016117948161357f565b60208082528101611794816135b8565b6020808252810161179481613604565b602080825281016117948161363d565b60208082528101611794816136aa565b60208082528101611794816136e3565b602080825281016117948161371c565b602080825281016117948161375f565b6020808252810161179481613798565b60208082528101611794816137d1565b602080825281016117948161380a565b60208082528101611794816138d8565b602080825281016117948161391c565b602080825281016117948161396f565b60208082528101611794816139d4565b6020808252810161179481613a4a565b6020808252810161179481613a83565b6020808252810161179481613ac7565b61012081016117948284613b00565b60a081016117948284613bac565b602081016117948284613c05565b60408101613ffd8286613c05565b8181036020830152613d0381848661322b565b60a0810161401e8288613c05565b61402b6020830187613c05565b6140386040830186613c05565b61404560608301856131ea565b6140526080830184613c05565b9695505050505050565b60808101613cdc8287613c05565b5190565b919050565b90815260200190565b60006117948261408c565b151590565b6001600160a01b031690565b90565b6000611794825b60006117948261407c565b82818337506000910152565b60005b838110156140d45781810151838201526020016140bc565b838111156129215750506000910152565b601f01601f191690565b6140f88161407c565b8114611c4357600080fd5b6140f881614087565b6140f88161409856fea365627a7a72315820f1ea1eb622e24a0bb282ea97c58c2af1618aae6dc2da9e0b325a32631013a6e66c6578706572696d656e74616cf564736f6c63430005110040

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.