Contract 0xf5c46d2193961bd041b54d7adf5c5ac67268e374

Contract Overview

Balance:
0 BNB
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968Mint BAI124228412021-09-16 17:56:591 hr 6 mins ago0xa0092d99e55c67fdea09bd0f5359af409e3d9fbd IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00466233
0x0ac735c14119267729c6320ef736125a94fc299ba85143f65a0c3a3e0b208de2Mint BAI124171502021-09-16 13:12:265 hrs 51 mins ago0xf5f9e169fafb2d58bc7c869a7ff958eb5ff75058 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00401566
0xd8edcd88032df755638eed8f8893be56f18a418c0e2022ce05e1ae47bd13b7bbMint BAI123896722021-09-15 14:17:551 day 4 hrs ago0xfbc67e012674037d5d98fb2600fe9acc8aadf6c1 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00576307
0xcfbe21805d37aea493af707733280903c75484adbff8c479bb04ac96b28200efRepay BAI123567422021-09-14 10:33:492 days 8 hrs ago0xfc21dbda49e638fdfe630f82bb6dbe78c0fe75da IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.0008274
0xb19d199afc9473ad5732690bf181a985f4cf59b45ff10d3754a2f3d0cf1cbe12Mint BAI123567262021-09-14 10:33:012 days 8 hrs ago0xfc21dbda49e638fdfe630f82bb6dbe78c0fe75da IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00427317
0x837678457401c0a36aa18bbc34c211c2aa437a4232bdcc104b75f00f0396f4faMint BAI123566762021-09-14 10:30:312 days 8 hrs ago0xfc21dbda49e638fdfe630f82bb6dbe78c0fe75da IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00412365
0x38abe0045d9d83acfab6695a42585c14d197935667f5dc460de319d4dc0a39f7Mint BAI123566682021-09-14 10:30:072 days 8 hrs ago0xfc21dbda49e638fdfe630f82bb6dbe78c0fe75da IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00442357
0x17fda56e8765b933da8b07973b8320965700e00f2e684e8c0f09a4f5bff03f88Repay BAI123222092021-09-13 5:32:593 days 13 hrs ago0xf8c7673d7caabfae733a12e8c7fd321517bd81ca IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.0006779
0xf71bb099625718d8b1d118c5a927be35e61956dd39f2386d5e063e7f6e3de6a3Repay BAI122244942021-09-09 19:27:576 days 23 hrs ago0xa0092d99e55c67fdea09bd0f5359af409e3d9fbd IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00067778
0x393d1106ed225cee1def00e3dae64e4df5a3fb418283ac79a80340165e29cf15Repay BAI121873862021-09-08 12:31:218 days 6 hrs ago0xfbc67e012674037d5d98fb2600fe9acc8aadf6c1 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.0006779
0xe28f344a0bf8e187e18061630af8ae32954ae931aabd08eb6c55477928896972Repay BAI121873292021-09-08 12:28:308 days 6 hrs ago0xfbc67e012674037d5d98fb2600fe9acc8aadf6c1 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00082788
0x20e4019cf3da8d38683d04fd9b2246168456288c8218467e930710ffcd6eed93Mint BAI121355592021-09-06 17:17:2310 days 1 hr ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558516
0x249665557bf8cd31c5d616484c307bbc2447dbb18fcff6c14d7065a0c9df7da0Mint BAI121330532021-09-06 15:12:0510 days 3 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558516
0xc9b79817f116e647b23641efb119ef2c02ef5b8fcb6ac275ad985568fe2a8247Repay BAI121108042021-09-05 20:22:3910 days 22 hrs ago0x96de760d5779913c423d7a885f4a0665b3792ffc IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00082764
0x5f7d1b69422f2c6f88548a7a447e7f3c04a3e461582a84c05ab6de38c447d153Mint BAI121056932021-09-05 16:07:0611 days 2 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558516
0xe6dddff9fae9a2cc71ce3750c3ef8e4d254426380241473a1764bbaee3ad2609Mint BAI121054722021-09-05 15:56:0311 days 3 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558516
0x885b57f206fe60c2386680a3fedc13beb0bceb4150f0372186c0cea86b7558d3Mint BAI120752082021-09-04 14:42:5112 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558528
0xe330b4b197286448004dae6fbc7214cf6248a6f02546f922bae6f247022650caMint BAI120751932021-09-04 14:42:0612 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00511239
0x177e0a57485629a8b473aaa1d3a0845481386cc60876b741f5423922e9bb22b0Mint BAI120751552021-09-04 14:40:1212 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00511239
0x5956191216919780187df074fbb0658f86af893b8489fce27f1365dcf1cb1868Mint BAI120751362021-09-04 14:39:1512 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00511239
0x3ac96439187cc3fd34826e329e1a878210e6bc2173c5d3ed90f74f5c14e4408dMint BAI120751312021-09-04 14:39:0012 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00511239
0xe604387bfb0e14279cc48bc17aa5979e14ebf25f578bc53b5887888ad735e0c2Mint BAI120751232021-09-04 14:38:3612 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00543528
0xdddb9cbd1aaa065df88b1e02818daac182a0ef7693418e131a867eee2f4d163eMint BAI120751132021-09-04 14:38:0612 days 4 hrs ago0xfe6c731063cf422063927a82f99e13eccf4d6a95 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00558528
0x117954c9d26c9fc50ba26c4bd8e0e41b225095bd8148015e596b235291793c16Mint BAI120672462021-09-04 7:44:0312 days 11 hrs ago0x8cb46f92cfd648676293ec158f735a5173cb29ff IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00483233
0x069dbf0960005995b19feeb75552004fdfa2f738da2fee1226b7b1f33facff37Mint BAI120512912021-09-03 17:52:1813 days 1 hr ago0x86b7ac18b9b27e05d9a25e591c197fa085fcb597 IN  0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB0.00263054
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x773f4515463d189500419aebe5e6932a79e43de60 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x0ba072fbdd03305117d893eaac5a0c852c2000010 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x91e3abc923a5a5f6d8d400b4114dda01c023fd2e0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x8ac5b7564f688971f8b690fe6ee0a0d54a2455890 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x5a55fe4f7187fb2a4cc30a70dc2e1a387a36e95b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0xcb8f3788ba780e0e7c3abecc2b015fae72c3fa8d0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x39deee3426032b1c4f09b5e52441863450a53a7b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x007f699b00fefb9d3572397f9afdbfda4447bc3b0 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a8 0xf5c46d2193961bd041b54d7adf5c5ac67268e3740 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
0xdd4f66a6762f1b9863b037c9a038a8dd6dbb4dff5b9209e838967bc921f3c968124228412021-09-16 17:56:591 hr 6 mins ago 0xf5c46d2193961bd041b54d7adf5c5ac67268e374 0x3b07f80703b9abd9f7d82ab082a4a29d64a3b5a80 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BAIController

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at BscScan.com on 2021-06-07
*/

// File: contracts/ComptrollerInterface.sol

pragma solidity ^0.5.16;

contract ComptrollerInterfaceG1 {
    /// @notice Indicator that this is a Comptroller contract (for inspection)
    bool public constant isComptroller = true;

    /*** Assets You Are In ***/

    function enterMarkets(address[] calldata bTokens) external returns (uint[] memory);
    function exitMarket(address bToken) external returns (uint);

    /*** Policy Hooks ***/

    function mintAllowed(address bToken, address minter, uint mintAmount) external returns (uint);
    function mintVerify(address bToken, address minter, uint mintAmount, uint mintTokens) external;

    function redeemAllowed(address bToken, address redeemer, uint redeemTokens) external returns (uint);
    function redeemVerify(address bToken, address redeemer, uint redeemAmount, uint redeemTokens) external;

    function borrowAllowed(address bToken, address borrower, uint borrowAmount) external returns (uint);
    function borrowVerify(address bToken, address borrower, uint borrowAmount) external;

    function repayBorrowAllowed(
        address bToken,
        address payer,
        address borrower,
        uint repayAmount) external returns (uint);
    function repayBorrowVerify(
        address bToken,
        address payer,
        address borrower,
        uint repayAmount,
        uint borrowerIndex) external;

    function liquidateBorrowAllowed(
        address bTokenBorrowed,
        address bTokenCollateral,
        address liquidator,
        address borrower,
        uint repayAmount) external returns (uint);
    function liquidateBorrowVerify(
        address bTokenBorrowed,
        address bTokenCollateral,
        address liquidator,
        address borrower,
        uint repayAmount,
        uint seizeTokens) external;

    function seizeAllowed(
        address bTokenCollateral,
        address bTokenBorrowed,
        address liquidator,
        address borrower,
        uint seizeTokens) external returns (uint);
    function seizeVerify(
        address bTokenCollateral,
        address bTokenBorrowed,
        address liquidator,
        address borrower,
        uint seizeTokens) external;

    function transferAllowed(address bToken, address src, address dst, uint transferTokens) external returns (uint);
    function transferVerify(address bToken, address src, address dst, uint transferTokens) external;

    /*** Liquidity/Liquidation Calculations ***/

    function liquidateCalculateSeizeTokens(
        address bTokenBorrowed,
        address bTokenCollateral,
        uint repayAmount) external view returns (uint, uint);
    function setMintedBAIOf(address owner, uint amount) external returns (uint);
}

contract ComptrollerInterfaceG2 is ComptrollerInterfaceG1 {
    function liquidateBAICalculateSeizeTokens(
        address bTokenCollateral,
        uint repayAmount) external view returns (uint, uint);
}

contract ComptrollerInterface is ComptrollerInterfaceG2 {
}

interface IBAIVault {
    function updatePendingRewards() external;
}

interface IComptroller {
    /*** Treasury Data ***/
    function treasuryAddress() external view returns (address);
    function treasuryPercent() external view returns (uint);
}

// File: contracts/InterestRateModel.sol

pragma solidity ^0.5.16;

/**
  * @title Bidao's InterestRateModel Interface
  * @author Bidao
  */
contract InterestRateModel {
    /// @notice Indicator that this is an InterestRateModel contract (for inspection)
    bool public constant isInterestRateModel = true;

    /**
      * @notice Calculates the current borrow interest rate per block
      * @param cash The total amount of cash the market has
      * @param borrows The total amount of borrows the market has outstanding
      * @param reserves The total amnount of reserves the market has
      * @return The borrow rate per block (as a percentage, and scaled by 1e18)
      */
    function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);

    /**
      * @notice Calculates the current supply interest rate per block
      * @param cash The total amount of cash the market has
      * @param borrows The total amount of borrows the market has outstanding
      * @param reserves The total amnount of reserves the market has
      * @param reserveFactorMantissa The current reserve factor the market has
      * @return The supply rate per block (as a percentage, and scaled by 1e18)
      */
    function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint);

}

// File: contracts/BTokenInterfaces.sol

pragma solidity ^0.5.16;



contract BTokenStorage {
    /**
     * @dev Guard variable for re-entrancy checks
     */
    bool internal _notEntered;

    /**
     * @notice EIP-20 token name for this token
     */
    string public name;

    /**
     * @notice EIP-20 token symbol for this token
     */
    string public symbol;

    /**
     * @notice EIP-20 token decimals for this token
     */
    uint8 public decimals;

    /**
     * @notice Maximum borrow rate that can ever be applied (.0005% / block)
     */

    uint internal constant borrowRateMaxMantissa = 0.0005e16;

    /**
     * @notice Maximum fraction of interest that can be set aside for reserves
     */
    uint internal constant reserveFactorMaxMantissa = 1e18;

    /**
     * @notice Administrator for this contract
     */
    address payable public admin;

    /**
     * @notice Pending administrator for this contract
     */
    address payable public pendingAdmin;

    /**
     * @notice Contract which oversees inter-bToken operations
     */
    ComptrollerInterface public comptroller;

    /**
     * @notice Model which tells what the current interest rate should be
     */
    InterestRateModel public interestRateModel;

    /**
     * @notice Initial exchange rate used when minting the first BTokens (used when totalSupply = 0)
     */
    uint internal initialExchangeRateMantissa;

    /**
     * @notice Fraction of interest currently set aside for reserves
     */
    uint public reserveFactorMantissa;

    /**
     * @notice Block number that interest was last accrued at
     */
    uint public accrualBlockNumber;

    /**
     * @notice Accumulator of the total earned interest rate since the opening of the market
     */
    uint public borrowIndex;

    /**
     * @notice Total amount of outstanding borrows of the underlying in this market
     */
    uint public totalBorrows;

    /**
     * @notice Total amount of reserves of the underlying held in this market
     */
    uint public totalReserves;

    /**
     * @notice Total number of tokens in circulation
     */
    uint public totalSupply;

    /**
     * @notice Official record of token balances for each account
     */
    mapping (address => uint) internal accountTokens;

    /**
     * @notice Approved token transfer amounts on behalf of others
     */
    mapping (address => mapping (address => uint)) internal transferAllowances;

    /**
     * @notice Container for borrow balance information
     * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
     * @member interestIndex Global borrowIndex as of the most recent balance-changing action
     */
    struct BorrowSnapshot {
        uint principal;
        uint interestIndex;
    }

    /**
     * @notice Mapping of account addresses to outstanding borrow balances
     */
    mapping(address => BorrowSnapshot) internal accountBorrows;
}

contract BTokenInterface is BTokenStorage {
    /**
     * @notice Indicator that this is a BToken contract (for inspection)
     */
    bool public constant isBToken = true;


    /*** Market Events ***/

    /**
     * @notice Event emitted when interest is accrued
     */
    event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows);

    /**
     * @notice Event emitted when tokens are minted
     */
    event Mint(address minter, uint mintAmount, uint mintTokens);

    /**
     * @notice Event emitted when tokens are redeemed
     */
    event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);

    /**
     * @notice Event emitted when tokens are redeemed and fee are transferred
     */
    event RedeemFee(address redeemer, uint feeAmount, uint redeemTokens);

    /**
     * @notice Event emitted when underlying is borrowed
     */
    event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows);

    /**
     * @notice Event emitted when a borrow is repaid
     */
    event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows);

    /**
     * @notice Event emitted when a borrow is liquidated
     */
    event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address bTokenCollateral, uint seizeTokens);


    /*** Admin Events ***/

    /**
     * @notice Event emitted when pendingAdmin is changed
     */
    event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

    /**
     * @notice Event emitted when pendingAdmin is accepted, which means admin is updated
     */
    event NewAdmin(address oldAdmin, address newAdmin);

    /**
     * @notice Event emitted when comptroller is changed
     */
    event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller);

    /**
     * @notice Event emitted when interestRateModel is changed
     */
    event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);

    /**
     * @notice Event emitted when the reserve factor is changed
     */
    event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa);

    /**
     * @notice Event emitted when the reserves are added
     */
    event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves);

    /**
     * @notice Event emitted when the reserves are reduced
     */
    event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves);

    /**
     * @notice EIP20 Transfer event
     */
    event Transfer(address indexed from, address indexed to, uint amount);

    /**
     * @notice EIP20 Approval event
     */
    event Approval(address indexed owner, address indexed spender, uint amount);

    /**
     * @notice Failure event
     */
    event Failure(uint error, uint info, uint detail);


    /*** User Interface ***/

    function transfer(address dst, uint amount) external returns (bool);
    function transferFrom(address src, address dst, uint amount) external returns (bool);
    function approve(address spender, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function balanceOfUnderlying(address owner) external returns (uint);
    function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
    function borrowRatePerBlock() external view returns (uint);
    function supplyRatePerBlock() external view returns (uint);
    function totalBorrowsCurrent() external returns (uint);
    function borrowBalanceCurrent(address account) external returns (uint);
    function borrowBalanceStored(address account) public view returns (uint);
    function exchangeRateCurrent() public returns (uint);
    function exchangeRateStored() public view returns (uint);
    function getCash() external view returns (uint);
    function accrueInterest() public returns (uint);
    function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint);


    /*** Admin Functions ***/

    function _setPendingAdmin(address payable newPendingAdmin) external returns (uint);
    function _acceptAdmin() external returns (uint);
    function _setComptroller(ComptrollerInterface newComptroller) public returns (uint);
    function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint);
    function _reduceReserves(uint reduceAmount) external returns (uint);
    function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint);
}

contract BBep20Storage {
    /**
     * @notice Underlying asset for this BToken
     */
    address public underlying;
}

contract BBep20Interface is BBep20Storage {

    /*** User Interface ***/

    function mint(uint mintAmount) external returns (uint);
    function redeem(uint redeemTokens) external returns (uint);
    function redeemUnderlying(uint redeemAmount) external returns (uint);
    function borrow(uint borrowAmount) external returns (uint);
    function repayBorrow(uint repayAmount) external returns (uint);
    function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
    function liquidateBorrow(address borrower, uint repayAmount, BTokenInterface bTokenCollateral) external returns (uint);


    /*** Admin Functions ***/

    function _addReserves(uint addAmount) external returns (uint);
}

contract VDelegationStorage {
    /**
     * @notice Implementation address for this contract
     */
    address public implementation;
}

contract VDelegatorInterface is VDelegationStorage {
    /**
     * @notice Emitted when implementation is changed
     */
    event NewImplementation(address oldImplementation, address newImplementation);

    /**
     * @notice Called by the admin to update the implementation of the delegator
     * @param implementation_ The address of the new implementation for delegation
     * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
     * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
     */
    function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public;
}

contract VDelegateInterface is VDelegationStorage {
    /**
     * @notice Called by the delegator on a delegate to initialize it for duty
     * @dev Should revert if any issues arise which make it unfit for delegation
     * @param data The encoded bytes data for any initialization
     */
    function _becomeImplementation(bytes memory data) public;

    /**
     * @notice Called by the delegator on a delegate to forfeit its responsibility
     */
    function _resignImplementation() public;
}

// File: contracts/ErrorReporter.sol

pragma solidity ^0.5.16;

contract ComptrollerErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        COMPTROLLER_MISMATCH,
        INSUFFICIENT_SHORTFALL,
        INSUFFICIENT_LIQUIDITY,
        INVALID_CLOSE_FACTOR,
        INVALID_COLLATERAL_FACTOR,
        INVALID_LIQUIDATION_INCENTIVE,
        MARKET_NOT_ENTERED, // no longer possible
        MARKET_NOT_LISTED,
        MARKET_ALREADY_LISTED,
        MATH_ERROR,
        NONZERO_BORROW_BALANCE,
        PRICE_ERROR,
        REJECTION,
        SNAPSHOT_ERROR,
        TOO_MANY_ASSETS,
        TOO_MUCH_REPAY,
        INSUFFICIENT_BALANCE_FOR_BAI
    }

    enum FailureInfo {
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
        EXIT_MARKET_BALANCE_OWED,
        EXIT_MARKET_REJECTION,
        SET_CLOSE_FACTOR_OWNER_CHECK,
        SET_CLOSE_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_OWNER_CHECK,
        SET_COLLATERAL_FACTOR_NO_EXISTS,
        SET_COLLATERAL_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
        SET_IMPLEMENTATION_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_VALIDATION,
        SET_MAX_ASSETS_OWNER_CHECK,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
        SET_PRICE_ORACLE_OWNER_CHECK,
        SUPPORT_MARKET_EXISTS,
        SUPPORT_MARKET_OWNER_CHECK,
        SET_PAUSE_GUARDIAN_OWNER_CHECK,
        SET_BAI_MINT_RATE_CHECK,
        SET_BAICONTROLLER_OWNER_CHECK,
        SET_MINTED_BAI_REJECTION,
        SET_TREASURY_OWNER_CHECK
    }

    /**
      * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
      * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
      **/
    event Failure(uint error, uint info, uint detail);

    /**
      * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
      */
    function fail(Error err, FailureInfo info) internal returns (uint) {
        emit Failure(uint(err), uint(info), 0);

        return uint(err);
    }

    /**
      * @dev use this when reporting an opaque error from an upgradeable collaborator contract
      */
    function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
        emit Failure(uint(err), uint(info), opaqueError);

        return uint(err);
    }
}

contract TokenErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        BAD_INPUT,
        COMPTROLLER_REJECTION,
        COMPTROLLER_CALCULATION_ERROR,
        INTEREST_RATE_MODEL_ERROR,
        INVALID_ACCOUNT_PAIR,
        INVALID_CLOSE_AMOUNT_REQUESTED,
        INVALID_COLLATERAL_FACTOR,
        MATH_ERROR,
        MARKET_NOT_FRESH,
        MARKET_NOT_LISTED,
        TOKEN_INSUFFICIENT_ALLOWANCE,
        TOKEN_INSUFFICIENT_BALANCE,
        TOKEN_INSUFFICIENT_CASH,
        TOKEN_TRANSFER_IN_FAILED,
        TOKEN_TRANSFER_OUT_FAILED,
        TOKEN_PRICE_ERROR
    }

    /*
     * Note: FailureInfo (but not Error) is kept in alphabetical order
     *       This is because FailureInfo grows significantly faster, and
     *       the order of Error has some meaning, while the order of FailureInfo
     *       is entirely arbitrary.
     */
    enum FailureInfo {
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED,
        ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED,
        ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
        BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        BORROW_ACCRUE_INTEREST_FAILED,
        BORROW_CASH_NOT_ABAILABLE,
        BORROW_FRESHNESS_CHECK,
        BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
        BORROW_MARKET_NOT_LISTED,
        BORROW_COMPTROLLER_REJECTION,
        LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
        LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
        LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
        LIQUIDATE_COMPTROLLER_REJECTION,
        LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED,
        LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX,
        LIQUIDATE_CLOSE_AMOUNT_IS_ZERO,
        LIQUIDATE_FRESHNESS_CHECK,
        LIQUIDATE_LIQUIDATOR_IS_BORROWER,
        LIQUIDATE_REPAY_BORROW_FRESH_FAILED,
        LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED,
        LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
        LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
        LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
        LIQUIDATE_SEIZE_TOO_MUCH,
        MINT_ACCRUE_INTEREST_FAILED,
        MINT_COMPTROLLER_REJECTION,
        MINT_EXCHANGE_CALCULATION_FAILED,
        MINT_EXCHANGE_RATE_READ_FAILED,
        MINT_FRESHNESS_CHECK,
        MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
        MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        MINT_TRANSFER_IN_FAILED,
        MINT_TRANSFER_IN_NOT_POSSIBLE,
        REDEEM_ACCRUE_INTEREST_FAILED,
        REDEEM_COMPTROLLER_REJECTION,
        REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED,
        REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED,
        REDEEM_EXCHANGE_RATE_READ_FAILED,
        REDEEM_FRESHNESS_CHECK,
        REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
        REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        REDEEM_TRANSFER_OUT_NOT_POSSIBLE,
        REDUCE_RESERVES_ACCRUE_INTEREST_FAILED,
        REDUCE_RESERVES_ADMIN_CHECK,
        REDUCE_RESERVES_CASH_NOT_ABAILABLE,
        REDUCE_RESERVES_FRESH_CHECK,
        REDUCE_RESERVES_VALIDATION,
        REPAY_BEHALF_ACCRUE_INTEREST_FAILED,
        REPAY_BORROW_ACCRUE_INTEREST_FAILED,
        REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_COMPTROLLER_REJECTION,
        REPAY_BORROW_FRESHNESS_CHECK,
        REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
        SET_COLLATERAL_FACTOR_OWNER_CHECK,
        SET_COLLATERAL_FACTOR_VALIDATION,
        SET_COMPTROLLER_OWNER_CHECK,
        SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED,
        SET_INTEREST_RATE_MODEL_FRESH_CHECK,
        SET_INTEREST_RATE_MODEL_OWNER_CHECK,
        SET_MAX_ASSETS_OWNER_CHECK,
        SET_ORACLE_MARKET_NOT_LISTED,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED,
        SET_RESERVE_FACTOR_ADMIN_CHECK,
        SET_RESERVE_FACTOR_FRESH_CHECK,
        SET_RESERVE_FACTOR_BOUNDS_CHECK,
        TRANSFER_COMPTROLLER_REJECTION,
        TRANSFER_NOT_ALLOWED,
        TRANSFER_NOT_ENOUGH,
        TRANSFER_TOO_MUCH,
        ADD_RESERVES_ACCRUE_INTEREST_FAILED,
        ADD_RESERVES_FRESH_CHECK,
        ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE,
        TOKEN_GET_UNDERLYING_PRICE_ERROR,
        REPAY_BAI_COMPTROLLER_REJECTION,
        REPAY_BAI_FRESHNESS_CHECK,
        BAI_MINT_EXCHANGE_CALCULATION_FAILED,
        SFT_MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
        REDEEM_FEE_CALCULATION_FAILED
    }

    /**
      * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
      * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
      **/
    event Failure(uint error, uint info, uint detail);

    /**
      * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
      */
    function fail(Error err, FailureInfo info) internal returns (uint) {
        emit Failure(uint(err), uint(info), 0);

        return uint(err);
    }

    /**
      * @dev use this when reporting an opaque error from an upgradeable collaborator contract
      */
    function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
        emit Failure(uint(err), uint(info), opaqueError);

        return uint(err);
    }
}

contract BAIControllerErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        REJECTION,
        SNAPSHOT_ERROR,
        PRICE_ERROR,
        MATH_ERROR,
        INSUFFICIENT_BALANCE_FOR_BAI
    }

    enum FailureInfo {
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
        SET_COMPTROLLER_OWNER_CHECK,
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
        BAI_MINT_REJECTION,
        BAI_BURN_REJECTION,
        BAI_LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
        BAI_LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
        BAI_LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
        BAI_LIQUIDATE_COMPTROLLER_REJECTION,
        BAI_LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED,
        BAI_LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX,
        BAI_LIQUIDATE_CLOSE_AMOUNT_IS_ZERO,
        BAI_LIQUIDATE_FRESHNESS_CHECK,
        BAI_LIQUIDATE_LIQUIDATOR_IS_BORROWER,
        BAI_LIQUIDATE_REPAY_BORROW_FRESH_FAILED,
        BAI_LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED,
        BAI_LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
        BAI_LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
        BAI_LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
        BAI_LIQUIDATE_SEIZE_TOO_MUCH,
        MINT_FEE_CALCULATION_FAILED,
        SET_TREASURY_OWNER_CHECK
    }

    /**
      * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
      * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
      **/
    event Failure(uint error, uint info, uint detail);

    /**
      * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
      */
    function fail(Error err, FailureInfo info) internal returns (uint) {
        emit Failure(uint(err), uint(info), 0);

        return uint(err);
    }

    /**
      * @dev use this when reporting an opaque error from an upgradeable collaborator contract
      */
    function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
        emit Failure(uint(err), uint(info), opaqueError);

        return uint(err);
    }
}

// File: contracts/CarefulMath.sol

pragma solidity ^0.5.16;

/**
  * @title Careful Math
  * @author Bidao
  * @notice Derived from OpenZeppelin's SafeMath library
  *         https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
  */
contract CarefulMath {

    /**
     * @dev Possible error codes that we can return
     */
    enum MathError {
        NO_ERROR,
        DIVISION_BY_ZERO,
        INTEGER_OVERFLOW,
        INTEGER_UNDERFLOW
    }

    /**
    * @dev Multiplies two numbers, returns an error on overflow.
    */
    function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (a == 0) {
            return (MathError.NO_ERROR, 0);
        }

        uint c = a * b;

        if (c / a != b) {
            return (MathError.INTEGER_OVERFLOW, 0);
        } else {
            return (MathError.NO_ERROR, c);
        }
    }

    /**
    * @dev Integer division of two numbers, truncating the quotient.
    */
    function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (b == 0) {
            return (MathError.DIVISION_BY_ZERO, 0);
        }

        return (MathError.NO_ERROR, a / b);
    }

    /**
    * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
    */
    function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (b <= a) {
            return (MathError.NO_ERROR, a - b);
        } else {
            return (MathError.INTEGER_UNDERFLOW, 0);
        }
    }

    /**
    * @dev Adds two numbers, returns an error on overflow.
    */
    function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
        uint c = a + b;

        if (c >= a) {
            return (MathError.NO_ERROR, c);
        } else {
            return (MathError.INTEGER_OVERFLOW, 0);
        }
    }

    /**
    * @dev add a and b and then subtract c
    */
    function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
        (MathError err0, uint sum) = addUInt(a, b);

        if (err0 != MathError.NO_ERROR) {
            return (err0, 0);
        }

        return subUInt(sum, c);
    }
}

// File: contracts/ExponentialNoError.sol

pragma solidity ^0.5.16;

/**
 * @title Exponential module for storing fixed-precision decimals
 * @author Compound
 * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
 *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
 *         `Exp({mantissa: 5100000000000000000})`.
 */
contract ExponentialNoError {
    uint constant expScale = 1e18;
    uint constant doubleScale = 1e36;
    uint constant halfExpScale = expScale/2;
    uint constant mantissaOne = expScale;

    struct Exp {
        uint mantissa;
    }

    struct Double {
        uint mantissa;
    }

    /**
     * @dev Truncates the given exp to a whole number value.
     *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
     */
    function truncate(Exp memory exp) pure internal returns (uint) {
        // Note: We are not using careful math here as we're performing a division that cannot fail
        return exp.mantissa / expScale;
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) {
        Exp memory product = mul_(a, scalar);
        return truncate(product);
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) {
        Exp memory product = mul_(a, scalar);
        return add_(truncate(product), addend);
    }

    /**
     * @dev Checks if first Exp is less than second Exp.
     */
    function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa < right.mantissa;
    }

    /**
     * @dev Checks if left Exp <= right Exp.
     */
    function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa <= right.mantissa;
    }

    /**
     * @dev Checks if left Exp > right Exp.
     */
    function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa > right.mantissa;
    }

    /**
     * @dev returns true if Exp is exactly zero
     */
    function isZeroExp(Exp memory value) pure internal returns (bool) {
        return value.mantissa == 0;
    }

    function safe224(uint n, string memory errorMessage) pure internal returns (uint224) {
        require(n < 2**224, errorMessage);
        return uint224(n);
    }

    function safe32(uint n, string memory errorMessage) pure internal returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }

    function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: add_(a.mantissa, b.mantissa)});
    }

    function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: add_(a.mantissa, b.mantissa)});
    }

    function add_(uint a, uint b) pure internal returns (uint) {
        return add_(a, b, "addition overflow");
    }

    function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
        uint c = a + b;
        require(c >= a, errorMessage);
        return c;
    }

    function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
    }

    function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: sub_(a.mantissa, b.mantissa)});
    }

    function sub_(uint a, uint b) pure internal returns (uint) {
        return sub_(a, b, "subtraction underflow");
    }

    function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
        require(b <= a, errorMessage);
        return a - b;
    }

    function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
    }

    function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
        return Exp({mantissa: mul_(a.mantissa, b)});
    }

    function mul_(uint a, Exp memory b) pure internal returns (uint) {
        return mul_(a, b.mantissa) / expScale;
    }

    function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
    }

    function mul_(Double memory a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: mul_(a.mantissa, b)});
    }

    function mul_(uint a, Double memory b) pure internal returns (uint) {
        return mul_(a, b.mantissa) / doubleScale;
    }

    function mul_(uint a, uint b) pure internal returns (uint) {
        return mul_(a, b, "multiplication overflow");
    }

    function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
        if (a == 0 || b == 0) {
            return 0;
        }
        uint c = a * b;
        require(c / a == b, errorMessage);
        return c;
    }

    function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
    }

    function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
        return Exp({mantissa: div_(a.mantissa, b)});
    }

    function div_(uint a, Exp memory b) pure internal returns (uint) {
        return div_(mul_(a, expScale), b.mantissa);
    }

    function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
    }

    function div_(Double memory a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: div_(a.mantissa, b)});
    }

    function div_(uint a, Double memory b) pure internal returns (uint) {
        return div_(mul_(a, doubleScale), b.mantissa);
    }

    function div_(uint a, uint b) pure internal returns (uint) {
        return div_(a, b, "divide by zero");
    }

    function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
        require(b > 0, errorMessage);
        return a / b;
    }

    function fraction(uint a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: div_(mul_(a, doubleScale), b)});
    }
}

// File: contracts/Exponential.sol

pragma solidity ^0.5.16;



/**
 * @title Exponential module for storing fixed-precision decimals
 * @author Bidao
 * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
 *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
 *         `Exp({mantissa: 5100000000000000000})`.
 */
contract Exponential is CarefulMath, ExponentialNoError {
    /**
     * @dev Creates an exponential from numerator and denominator values.
     *      Note: Returns an error if (`num` * 10e18) > MAX_INT,
     *            or if `denom` is zero.
     */
    function getExp(uint num, uint denom) internal pure returns (MathError, Exp memory) {
        (MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        (MathError err1, uint rational) = divUInt(scaledNumerator, denom);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: rational}));
    }

    /**
     * @dev Adds two exponentials, returning a new exponential.
     */
    function addExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) {
        (MathError error, uint result) = addUInt(a.mantissa, b.mantissa);

        return (error, Exp({mantissa: result}));
    }

    /**
     * @dev Subtracts two exponentials, returning a new exponential.
     */
    function subExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) {
        (MathError error, uint result) = subUInt(a.mantissa, b.mantissa);

        return (error, Exp({mantissa: result}));
    }

    /**
     * @dev Multiply an Exp by a scalar, returning a new Exp.
     */
    function mulScalar(Exp memory a, uint scalar) internal pure returns (MathError, Exp memory) {
        (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mulScalarTruncate(Exp memory a, uint scalar) internal pure returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return (MathError.NO_ERROR, truncate(product));
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) internal pure returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return addUInt(truncate(product), addend);
    }

    /**
     * @dev Divide an Exp by a scalar, returning a new Exp.
     */
    function divScalar(Exp memory a, uint scalar) internal pure returns (MathError, Exp memory) {
        (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa}));
    }

    /**
     * @dev Divide a scalar by an Exp, returning a new Exp.
     */
    function divScalarByExp(uint scalar, Exp memory divisor) internal pure returns (MathError, Exp memory) {
        /*
          We are doing this as:
          getExp(mulUInt(expScale, scalar), divisor.mantissa)

          How it works:
          Exp = a / b;
          Scalar = s;
          `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
        */
        (MathError err0, uint numerator) = mulUInt(expScale, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }
        return getExp(numerator, divisor.mantissa);
    }

    /**
     * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer.
     */
    function divScalarByExpTruncate(uint scalar, Exp memory divisor) internal pure returns (MathError, uint) {
        (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return (MathError.NO_ERROR, truncate(fraction));
    }

    /**
     * @dev Multiplies two exponentials, returning a new exponential.
     */
    function mulExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) {

        (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        // We add half the scale before dividing so that we get rounding instead of truncation.
        //  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
        // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
        (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
        // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
        assert(err2 == MathError.NO_ERROR);

        return (MathError.NO_ERROR, Exp({mantissa: product}));
    }

    /**
     * @dev Multiplies two exponentials given their mantissas, returning a new exponential.
     */
    function mulExp(uint a, uint b) internal pure returns (MathError, Exp memory) {
        return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
    }

    /**
     * @dev Multiplies three exponentials, returning a new exponential.
     */
    function mulExp3(Exp memory a, Exp memory b, Exp memory c) internal pure returns (MathError, Exp memory) {
        (MathError err, Exp memory ab) = mulExp(a, b);
        if (err != MathError.NO_ERROR) {
            return (err, ab);
        }
        return mulExp(ab, c);
    }

    /**
     * @dev Divides two exponentials, returning a new exponential.
     *     (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
     *  which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
     */
    function divExp(Exp memory a, Exp memory b) internal pure returns (MathError, Exp memory) {
        return getExp(a.mantissa, b.mantissa);
    }
}

// File: contracts/EIP20Interface.sol

pragma solidity ^0.5.16;

/**
 * @title BEP 20 Token Standard Interface
 *  https://eips.ethereum.org/EIPS/eip-20
 */
interface EIP20Interface {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    /**
      * @notice Get the total number of tokens in circulation
      * @return The supply of tokens
      */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
      * @notice Transfer `amount` tokens from `msg.sender` to `dst`
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return Whether or not the transfer succeeded
      */
    function transfer(address dst, uint256 amount) external returns (bool success);

    /**
      * @notice Transfer `amount` tokens from `src` to `dst`
      * @param src The address of the source account
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return Whether or not the transfer succeeded
      */
    function transferFrom(address src, address dst, uint256 amount) external returns (bool success);

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved (-1 means infinite)
      * @return Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return The number of tokens allowed to be spent (-1 means infinite)
      */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

// File: contracts/EIP20NonStandardInterface.sol

pragma solidity ^0.5.16;

/**
 * @title EIP20NonStandardInterface
 * @dev Version of BEP20 with no return values for `transfer` and `transferFrom`
 *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */
interface EIP20NonStandardInterface {

    /**
     * @notice Get the total number of tokens in circulation
     * @return The supply of tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transfer` does not return a value, in violation of the BEP-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
      * @notice Transfer `amount` tokens from `msg.sender` to `dst`
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      */
    function transfer(address dst, uint256 amount) external;

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the BEP-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
      * @notice Transfer `amount` tokens from `src` to `dst`
      * @param src The address of the source account
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      */
    function transferFrom(address src, address dst, uint256 amount) external;

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved
      * @return Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return The number of tokens allowed to be spent
      */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

// File: contracts/BToken.sol

pragma solidity ^0.5.16;








/**
 * @title Bidao's BToken Contract
 * @notice Abstract base for BTokens
 * @author Bidao
 */
contract BToken is BTokenInterface, Exponential, TokenErrorReporter {
    /**
     * @notice Initialize the money market
     * @param comptroller_ The address of the Comptroller
     * @param interestRateModel_ The address of the interest rate model
     * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
     * @param name_ EIP-20 name of this token
     * @param symbol_ EIP-20 symbol of this token
     * @param decimals_ EIP-20 decimal precision of this token
     */
    function initialize(ComptrollerInterface comptroller_,
                        InterestRateModel interestRateModel_,
                        uint initialExchangeRateMantissa_,
                        string memory name_,
                        string memory symbol_,
                        uint8 decimals_) public {
        require(msg.sender == admin, "only admin may initialize the market");
        require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");

        // Set initial exchange rate
        initialExchangeRateMantissa = initialExchangeRateMantissa_;
        require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");

        // Set the comptroller
        uint err = _setComptroller(comptroller_);
        require(err == uint(Error.NO_ERROR), "setting comptroller failed");

        // Initialize block number and borrow index (block number mocks depend on comptroller being set)
        accrualBlockNumber = getBlockNumber();
        borrowIndex = mantissaOne;

        // Set the interest rate model (depends on block number / borrow index)
        err = _setInterestRateModelFresh(interestRateModel_);
        require(err == uint(Error.NO_ERROR), "setting interest rate model failed");

        name = name_;
        symbol = symbol_;
        decimals = decimals_;

        // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
        _notEntered = true;
    }

    /**
     * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
     * @dev Called by both `transfer` and `transferFrom` internally
     * @param spender The address of the account performing the transfer
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param tokens The number of tokens to transfer
     * @return Whether or not the transfer succeeded
     */
    function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) {
        /* Fail if transfer not allowed */
        uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens);
        if (allowed != 0) {
            return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed);
        }

        /* Do not allow self-transfers */
        if (src == dst) {
            return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED);
        }

        /* Get the allowance, infinite for the account owner */
        uint startingAllowance = 0;
        if (spender == src) {
            startingAllowance = uint(-1);
        } else {
            startingAllowance = transferAllowances[src][spender];
        }

        /* Do the calculations, checking for {under,over}flow */
        MathError mathErr;
        uint allowanceNew;
        uint srbTokensNew;
        uint dstTokensNew;

        (mathErr, allowanceNew) = subUInt(startingAllowance, tokens);
        if (mathErr != MathError.NO_ERROR) {
            return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ALLOWED);
        }

        (mathErr, srbTokensNew) = subUInt(accountTokens[src], tokens);
        if (mathErr != MathError.NO_ERROR) {
            return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH);
        }

        (mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens);
        if (mathErr != MathError.NO_ERROR) {
            return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        accountTokens[src] = srbTokensNew;
        accountTokens[dst] = dstTokensNew;

        /* Eat some of the allowance (if necessary) */
        if (startingAllowance != uint(-1)) {
            transferAllowances[src][spender] = allowanceNew;
        }

        /* We emit a Transfer event */
        emit Transfer(src, dst, tokens);

        comptroller.transferVerify(address(this), src, dst, tokens);

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return Whether or not the transfer succeeded
     */
    function transfer(address dst, uint256 amount) external nonReentrant returns (bool) {
        return transferTokens(msg.sender, msg.sender, dst, amount) == uint(Error.NO_ERROR);
    }

    /**
     * @notice Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return Whether or not the transfer succeeded
     */
    function transferFrom(address src, address dst, uint256 amount) external nonReentrant returns (bool) {
        return transferTokens(msg.sender, src, dst, amount) == uint(Error.NO_ERROR);
    }

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved (-1 means infinite)
     * @return Whether or not the approval succeeded
     */
    function approve(address spender, uint256 amount) external returns (bool) {
        address src = msg.sender;
        transferAllowances[src][spender] = amount;
        emit Approval(src, spender, amount);
        return true;
    }

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return The number of tokens allowed to be spent (-1 means infinite)
     */
    function allowance(address owner, address spender) external view returns (uint256) {
        return transferAllowances[owner][spender];
    }

    /**
     * @notice Get the token balance of the `owner`
     * @param owner The address of the account to query
     * @return The number of tokens owned by `owner`
     */
    function balanceOf(address owner) external view returns (uint256) {
        return accountTokens[owner];
    }

    /**
     * @notice Get the underlying balance of the `owner`
     * @dev This also accrues interest in a transaction
     * @param owner The address of the account to query
     * @return The amount of underlying owned by `owner`
     */
    function balanceOfUnderlying(address owner) external returns (uint) {
        Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
        (MathError mErr, uint balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
        require(mErr == MathError.NO_ERROR, "balance could not be calculated");
        return balance;
    }

    /**
     * @notice Get a snapshot of the account's balances, and the cached exchange rate
     * @dev This is used by comptroller to more efficiently perform liquidity checks.
     * @param account Address of the account to snapshot
     * @return (possible error, token balance, borrow balance, exchange rate mantissa)
     */
    function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) {
        uint bTokenBalance = accountTokens[account];
        uint borrowBalance;
        uint exchangeRateMantissa;

        MathError mErr;

        (mErr, borrowBalance) = borrowBalanceStoredInternal(account);
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.MATH_ERROR), 0, 0, 0);
        }

        (mErr, exchangeRateMantissa) = exchangeRateStoredInternal();
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.MATH_ERROR), 0, 0, 0);
        }

        return (uint(Error.NO_ERROR), bTokenBalance, borrowBalance, exchangeRateMantissa);
    }

    /**
     * @dev Function to simply retrieve block number
     *  This exists mainly for inheriting test contracts to stub this result.
     */
    function getBlockNumber() internal view returns (uint) {
        return block.number;
    }

    /**
     * @notice Returns the current per-block borrow interest rate for this bToken
     * @return The borrow interest rate per block, scaled by 1e18
     */
    function borrowRatePerBlock() external view returns (uint) {
        return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves);
    }

    /**
     * @notice Returns the current per-block supply interest rate for this bToken
     * @return The supply interest rate per block, scaled by 1e18
     */
    function supplyRatePerBlock() external view returns (uint) {
        return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa);
    }

    /**
     * @notice Returns the current total borrows plus accrued interest
     * @return The total borrows with interest
     */
    function totalBorrowsCurrent() external nonReentrant returns (uint) {
        require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
        return totalBorrows;
    }

    /**
     * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
     * @param account The address whose balance should be calculated after updating borrowIndex
     * @return The calculated balance
     */
    function borrowBalanceCurrent(address account) external nonReentrant returns (uint) {
        require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
        return borrowBalanceStored(account);
    }

    /**
     * @notice Return the borrow balance of account based on stored data
     * @param account The address whose balance should be calculated
     * @return The calculated balance
     */
    function borrowBalanceStored(address account) public view returns (uint) {
        (MathError err, uint result) = borrowBalanceStoredInternal(account);
        require(err == MathError.NO_ERROR, "borrowBalanceStored: borrowBalanceStoredInternal failed");
        return result;
    }

    /**
     * @notice Return the borrow balance of account based on stored data
     * @param account The address whose balance should be calculated
     * @return (error code, the calculated balance or 0 if error code is non-zero)
     */
    function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint) {
        /* Note: we do not assert that the market is up to date */
        MathError mathErr;
        uint principalTimesIndex;
        uint result;

        /* Get borrowBalance and borrowIndex */
        BorrowSnapshot storage borrowSnapshot = accountBorrows[account];

        /* If borrowBalance = 0 then borrowIndex is likely also 0.
         * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
         */
        if (borrowSnapshot.principal == 0) {
            return (MathError.NO_ERROR, 0);
        }

        /* Calculate new borrow balance using the interest index:
         *  recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
         */
        (mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex);
        if (mathErr != MathError.NO_ERROR) {
            return (mathErr, 0);
        }

        (mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex);
        if (mathErr != MathError.NO_ERROR) {
            return (mathErr, 0);
        }

        return (MathError.NO_ERROR, result);
    }

    /**
     * @notice Accrue interest then return the up-to-date exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateCurrent() public nonReentrant returns (uint) {
        require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
        return exchangeRateStored();
    }

    /**
     * @notice Calculates the exchange rate from the underlying to the BToken
     * @dev This function does not accrue interest before calculating the exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateStored() public view returns (uint) {
        (MathError err, uint result) = exchangeRateStoredInternal();
        require(err == MathError.NO_ERROR, "exchangeRateStored: exchangeRateStoredInternal failed");
        return result;
    }

    /**
     * @notice Calculates the exchange rate from the underlying to the BToken
     * @dev This function does not accrue interest before calculating the exchange rate
     * @return (error code, calculated exchange rate scaled by 1e18)
     */
    function exchangeRateStoredInternal() internal view returns (MathError, uint) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            /*
             * If there are no tokens minted:
             *  exchangeRate = initialExchangeRate
             */
            return (MathError.NO_ERROR, initialExchangeRateMantissa);
        } else {
            /*
             * Otherwise:
             *  exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
             */
            uint totalCash = getCashPrior();
            uint cashPlusBorrowsMinusReserves;
            Exp memory exchangeRate;
            MathError mathErr;

            (mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves);
            if (mathErr != MathError.NO_ERROR) {
                return (mathErr, 0);
            }

            (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply);
            if (mathErr != MathError.NO_ERROR) {
                return (mathErr, 0);
            }

            return (MathError.NO_ERROR, exchangeRate.mantissa);
        }
    }

    /**
     * @notice Get cash balance of this bToken in the underlying asset
     * @return The quantity of underlying asset owned by this contract
     */
    function getCash() external view returns (uint) {
        return getCashPrior();
    }

    /**
     * @notice Applies accrued interest to total borrows and reserves
     * @dev This calculates interest accrued from the last checkpointed block
     *   up to the current block and writes new checkpoint to storage.
     */
    function accrueInterest() public returns (uint) {
        /* Remember the initial block number */
        uint currentBlockNumber = getBlockNumber();
        uint accrualBlockNumberPrior = accrualBlockNumber;

        /* Short-circuit accumulating 0 interest */
        if (accrualBlockNumberPrior == currentBlockNumber) {
            return uint(Error.NO_ERROR);
        }

        /* Read the previous values out of storage */
        uint cashPrior = getCashPrior();
        uint borrowsPrior = totalBorrows;
        uint reservesPrior = totalReserves;
        uint borrowIndexPrior = borrowIndex;

        /* Calculate the current borrow interest rate */
        uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
        require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");

        /* Calculate the number of blocks elapsed since the last accrual */
        (MathError mathErr, uint blockDelta) = subUInt(currentBlockNumber, accrualBlockNumberPrior);
        require(mathErr == MathError.NO_ERROR, "could not calculate block delta");

        /*
         * Calculate the interest accumulated into borrows and reserves and the new index:
         *  simpleInterestFactor = borrowRate * blockDelta
         *  interestAccumulated = simpleInterestFactor * totalBorrows
         *  totalBorrowsNew = interestAccumulated + totalBorrows
         *  totalReservesNew = interestAccumulated * reserveFactor + totalReserves
         *  borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
         */

        Exp memory simpleInterestFactor;
        uint interestAccumulated;
        uint totalBorrowsNew;
        uint totalReservesNew;
        uint borrowIndexNew;

        (mathErr, simpleInterestFactor) = mulScalar(Exp({mantissa: borrowRateMantissa}), blockDelta);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, uint(mathErr));
        }

        (mathErr, interestAccumulated) = mulScalarTruncate(simpleInterestFactor, borrowsPrior);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint(mathErr));
        }

        (mathErr, totalBorrowsNew) = addUInt(interestAccumulated, borrowsPrior);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint(mathErr));
        }

        (mathErr, totalReservesNew) = mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint(mathErr));
        }

        (mathErr, borrowIndexNew) = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint(mathErr));
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We write the previously calculated values into storage */
        accrualBlockNumber = currentBlockNumber;
        borrowIndex = borrowIndexNew;
        totalBorrows = totalBorrowsNew;
        totalReserves = totalReservesNew;

        /* We emit an AccrueInterest event */
        emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Sender supplies assets into the market and receives bTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param mintAmount The amount of the underlying asset to supply
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
     */
    function mintInternal(uint mintAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0);
        }
        // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
        return mintFresh(msg.sender, mintAmount);
    }

    struct MintLocalVars {
        Error err;
        MathError mathErr;
        uint exchangeRateMantissa;
        uint mintTokens;
        uint totalSupplyNew;
        uint accountTokensNew;
        uint actualMintAmount;
    }

    /**
     * @notice User supplies assets into the market and receives bTokens in exchange
     * @dev Assumes interest has already been accrued up to the current block
     * @param minter The address of the account which is supplying the assets
     * @param mintAmount The amount of the underlying asset to supply
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
     */
    function mintFresh(address minter, uint mintAmount) internal returns (uint, uint) {
        /* Fail if mint not allowed */
        uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount);
        if (allowed != 0) {
            return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0);
        }

        MintLocalVars memory vars;

        (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
        if (vars.mathErr != MathError.NO_ERROR) {
            return (failOpaque(Error.MATH_ERROR, FailureInfo.MINT_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr)), 0);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         *  We call `doTransferIn` for the minter and the mintAmount.
         *  Note: The bToken must handle variations between BEP-20 and BNB underlying.
         *  `doTransferIn` reverts if anything goes wrong, since we can't be sure if
         *  side-effects occurred. The function returns the amount actually transferred,
         *  in case of a fee. On success, the bToken holds an additional `actualMintAmount`
         *  of cash.
         */
        vars.actualMintAmount = doTransferIn(minter, mintAmount);

        /*
         * We get the current exchange rate and calculate the number of bTokens to be minted:
         *  mintTokens = actualMintAmount / exchangeRate
         */

        (vars.mathErr, vars.mintTokens) = divScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa}));
        require(vars.mathErr == MathError.NO_ERROR, "MINT_EXCHANGE_CALCULATION_FAILED");

        /*
         * We calculate the new total supply of bTokens and minter token balance, checking for overflow:
         *  totalSupplyNew = totalSupply + mintTokens
         *  accountTokensNew = accountTokens[minter] + mintTokens
         */
        (vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens);
        require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED");

        (vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens);
        require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED");

        /* We write previously calculated values into storage */
        totalSupply = vars.totalSupplyNew;
        accountTokens[minter] = vars.accountTokensNew;

        /* We emit a Mint event, and a Transfer event */
        emit Mint(minter, vars.actualMintAmount, vars.mintTokens);
        emit Transfer(address(this), minter, vars.mintTokens);

        /* We call the defense hook */
        comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens);

        return (uint(Error.NO_ERROR), vars.actualMintAmount);
    }

    /**
     * @notice Sender redeems bTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemTokens The number of bTokens to redeem into underlying
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeemInternal(uint redeemTokens) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
            return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
        }
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        return redeemFresh(msg.sender, redeemTokens, 0);
    }

    /**
     * @notice Sender redeems bTokens in exchange for a specified amount of underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemAmount The amount of underlying to receive from redeeming bTokens
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
            return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
        }
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        return redeemFresh(msg.sender, 0, redeemAmount);
    }

    struct RedeemLocalVars {
        Error err;
        MathError mathErr;
        uint exchangeRateMantissa;
        uint redeemTokens;
        uint redeemAmount;
        uint totalSupplyNew;
        uint accountTokensNew;
    }

    /**
     * @notice User redeems bTokens in exchange for the underlying asset
     * @dev Assumes interest has already been accrued up to the current block
     * @param redeemer The address of the account which is redeeming the tokens
     * @param redeemTokensIn The number of bTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
     * @param redeemAmountIn The number of underlying tokens to receive from redeeming bTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) {
        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");

        RedeemLocalVars memory vars;

        /* exchangeRate = invoke Exchange Rate Stored() */
        (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr));
        }

        /* If redeemTokensIn > 0: */
        if (redeemTokensIn > 0) {
            /*
             * We calculate the exchange rate and the amount of underlying to be redeemed:
             *  redeemTokens = redeemTokensIn
             *  redeemAmount = redeemTokensIn x exchangeRateCurrent
             */
            vars.redeemTokens = redeemTokensIn;

            (vars.mathErr, vars.redeemAmount) = mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn);
            if (vars.mathErr != MathError.NO_ERROR) {
                return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint(vars.mathErr));
            }
        } else {
            /*
             * We get the current exchange rate and calculate the amount to be redeemed:
             *  redeemTokens = redeemAmountIn / exchangeRate
             *  redeemAmount = redeemAmountIn
             */

            (vars.mathErr, vars.redeemTokens) = divScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa}));
            if (vars.mathErr != MathError.NO_ERROR) {
                return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, uint(vars.mathErr));
            }

            vars.redeemAmount = redeemAmountIn;
        }

        /* Fail if redeem not allowed */
        uint allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens);
        if (allowed != 0) {
            return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK);
        }

        /*
         * We calculate the new total supply and redeemer balance, checking for underflow:
         *  totalSupplyNew = totalSupply - redeemTokens
         *  accountTokensNew = accountTokens[redeemer] - redeemTokens
         */
        (vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens);
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, uint(vars.mathErr));
        }

        (vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens);
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
        }

        /* Fail gracefully if protocol has insufficient cash */
        if (getCashPrior() < vars.redeemAmount) {
            return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We invoke doTransferOut for the redeemer and the redeemAmount.
         *  Note: The bToken must handle variations between BEP-20 and BNB underlying.
         *  On success, the bToken has redeemAmount less of cash.
         *  doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
         */

        uint feeAmount;
        uint remainedAmount;
        if (IComptroller(address(comptroller)).treasuryPercent() != 0) {
            (vars.mathErr, feeAmount) = mulUInt(vars.redeemAmount, IComptroller(address(comptroller)).treasuryPercent());
            if (vars.mathErr != MathError.NO_ERROR) {
                return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_FEE_CALCULATION_FAILED, uint(vars.mathErr));
            }

            (vars.mathErr, feeAmount) = divUInt(feeAmount, 1e18);
            if (vars.mathErr != MathError.NO_ERROR) {
                return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_FEE_CALCULATION_FAILED, uint(vars.mathErr));
            }

            (vars.mathErr, remainedAmount) = subUInt(vars.redeemAmount, feeAmount);
            if (vars.mathErr != MathError.NO_ERROR) {
                return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_FEE_CALCULATION_FAILED, uint(vars.mathErr));
            }

            doTransferOut(address(uint160(IComptroller(address(comptroller)).treasuryAddress())), feeAmount);

            emit RedeemFee(redeemer, feeAmount, vars.redeemTokens);
        } else {
            remainedAmount = vars.redeemAmount;
        }

        doTransferOut(redeemer, remainedAmount);

        /* We write previously calculated values into storage */
        totalSupply = vars.totalSupplyNew;
        accountTokens[redeemer] = vars.accountTokensNew;

        /* We emit a Transfer event, and a Redeem event */
        emit Transfer(redeemer, address(this), vars.redeemTokens);
        emit Redeem(redeemer, remainedAmount, vars.redeemTokens);

        /* We call the defense hook */
        comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens);

        return uint(Error.NO_ERROR);
    }

    /**
      * @notice Sender borrows assets from the protocol to their own address
      * @param borrowAmount The amount of the underlying asset to borrow
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function borrowInternal(uint borrowAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED);
        }
        // borrowFresh emits borrow-specific logs on errors, so we don't need to
        return borrowFresh(msg.sender, borrowAmount);
    }

    struct BorrowLocalVars {
        MathError mathErr;
        uint accountBorrows;
        uint accountBorrowsNew;
        uint totalBorrowsNew;
    }

    /**
      * @notice Users borrow assets from the protocol to their own address
      * @param borrowAmount The amount of the underlying asset to borrow
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function borrowFresh(address payable borrower, uint borrowAmount) internal returns (uint) {
        /* Fail if borrow not allowed */
        uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount);
        if (allowed != 0) {
            return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK);
        }

        /* Fail gracefully if protocol has insufficient underlying cash */
        if (getCashPrior() < borrowAmount) {
            return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_ABAILABLE);
        }

        BorrowLocalVars memory vars;

        /*
         * We calculate the new borrower and total borrow balances, failing on overflow:
         *  accountBorrowsNew = accountBorrows + borrowAmount
         *  totalBorrowsNew = totalBorrows + borrowAmount
         */
        (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
        }

        (vars.mathErr, vars.accountBorrowsNew) = addUInt(vars.accountBorrows, borrowAmount);
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
        }

        (vars.mathErr, vars.totalBorrowsNew) = addUInt(totalBorrows, borrowAmount);
        if (vars.mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We invoke doTransferOut for the borrower and the borrowAmount.
         *  Note: The bToken must handle variations between BEP-20 and BNB underlying.
         *  On success, the bToken borrowAmount less of cash.
         *  doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
         */
        doTransferOut(borrower, borrowAmount);

        /* We write the previously calculated values into storage */
        accountBorrows[borrower].principal = vars.accountBorrowsNew;
        accountBorrows[borrower].interestIndex = borrowIndex;
        totalBorrows = vars.totalBorrowsNew;

        /* We emit a Borrow event */
        emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);

        /* We call the defense hook */
        comptroller.borrowVerify(address(this), borrower, borrowAmount);

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0);
        }
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        return repayBorrowFresh(msg.sender, msg.sender, repayAmount);
    }

    /**
     * @notice Sender repays a borrow belonging to borrower
     * @param borrower the account with the debt being payed off
     * @param repayAmount The amount to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0);
        }
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        return repayBorrowFresh(msg.sender, borrower, repayAmount);
    }

    struct RepayBorrowLocalVars {
        Error err;
        MathError mathErr;
        uint repayAmount;
        uint borrowerIndex;
        uint accountBorrows;
        uint accountBorrowsNew;
        uint totalBorrowsNew;
        uint actualRepayAmount;
    }

    /**
     * @notice Borrows are repaid by another user (possibly the borrower).
     * @param payer the account paying off the borrow
     * @param borrower the account with the debt being payed off
     * @param repayAmount the amount of undelrying tokens being returned
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint, uint) {
        /* Fail if repayBorrow not allowed */
        uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount);
        if (allowed != 0) {
            return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0);
        }

        RepayBorrowLocalVars memory vars;

        /* We remember the original borrowerIndex for verification purposes */
        vars.borrowerIndex = accountBorrows[borrower].interestIndex;

        /* We fetch the amount the borrower owes, with accumulated interest */
        (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
        if (vars.mathErr != MathError.NO_ERROR) {
            return (failOpaque(Error.MATH_ERROR, FailureInfo.REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)), 0);
        }

        /* If repayAmount == -1, repayAmount = accountBorrows */
        if (repayAmount == uint(-1)) {
            vars.repayAmount = vars.accountBorrows;
        } else {
            vars.repayAmount = repayAmount;
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We call doTransferIn for the payer and the repayAmount
         *  Note: The bToken must handle variations between BEP-20 and BNB underlying.
         *  On success, the bToken holds an additional repayAmount of cash.
         *  doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
         *   it returns the amount actually transferred, in case of a fee.
         */
        vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount);

        /*
         * We calculate the new borrower and total borrow balances, failing on underflow:
         *  accountBorrowsNew = accountBorrows - actualRepayAmount
         *  totalBorrowsNew = totalBorrows - actualRepayAmount
         */
        (vars.mathErr, vars.accountBorrowsNew) = subUInt(vars.accountBorrows, vars.actualRepayAmount);
        require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED");

        (vars.mathErr, vars.totalBorrowsNew) = subUInt(totalBorrows, vars.actualRepayAmount);
        require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED");

        /* We write the previously calculated values into storage */
        accountBorrows[borrower].principal = vars.accountBorrowsNew;
        accountBorrows[borrower].interestIndex = borrowIndex;
        totalBorrows = vars.totalBorrowsNew;

        /* We emit a RepayBorrow event */
        emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);

        /* We call the defense hook */
        comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex);

        return (uint(Error.NO_ERROR), vars.actualRepayAmount);
    }

    /**
     * @notice The sender liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this bToken to be liquidated
     * @param bTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function liquidateBorrowInternal(address borrower, uint repayAmount, BTokenInterface bTokenCollateral) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0);
        }

        error = bTokenCollateral.accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0);
        }

        // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
        return liquidateBorrowFresh(msg.sender, borrower, repayAmount, bTokenCollateral);
    }

    /**
     * @notice The liquidator liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this bToken to be liquidated
     * @param liquidator The address repaying the borrow and seizing collateral
     * @param bTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, BTokenInterface bTokenCollateral) internal returns (uint, uint) {
        /* Fail if liquidate not allowed */
        uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(bTokenCollateral), liquidator, borrower, repayAmount);
        if (allowed != 0) {
            return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0);
        }

        /* Verify bTokenCollateral market's block number equals current block number */
        if (bTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
            return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0);
        }

        /* Fail if borrower = liquidator */
        if (borrower == liquidator) {
            return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0);
        }

        /* Fail if repayAmount = 0 */
        if (repayAmount == 0) {
            return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0);
        }

        /* Fail if repayAmount = -1 */
        if (repayAmount == uint(-1)) {
            return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0);
        }


        /* Fail if repayBorrow fails */
        (uint repayBorrowError, uint actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount);
        if (repayBorrowError != uint(Error.NO_ERROR)) {
            return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We calculate the number of collateral tokens that will be seized */
        (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(bTokenCollateral), actualRepayAmount);
        require(amountSeizeError == uint(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");

        /* Revert if borrower collateral token balance < seizeTokens */
        require(bTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");

        // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
        uint seizeError;
        if (address(bTokenCollateral) == address(this)) {
            seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens);
        } else {
            seizeError = bTokenCollateral.seize(liquidator, borrower, seizeTokens);
        }

        /* Revert if seize tokens fails (since we cannot be sure of side effects) */
        require(seizeError == uint(Error.NO_ERROR), "token seizure failed");

        /* We emit a LiquidateBorrow event */
        emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(bTokenCollateral), seizeTokens);

        /* We call the defense hook */
        comptroller.liquidateBorrowVerify(address(this), address(bTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens);

        return (uint(Error.NO_ERROR), actualRepayAmount);
    }

    /**
     * @notice Transfers collateral tokens (this market) to the liquidator.
     * @dev Will fail unless called by another bToken during the process of liquidation.
     *  Its absolutely critical to use msg.sender as the borrowed bToken and not a parameter.
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of bTokens to seize
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function seize(address liquidator, address borrower, uint seizeTokens) external nonReentrant returns (uint) {
        return seizeInternal(msg.sender, liquidator, borrower, seizeTokens);
    }

    /**
     * @notice Transfers collateral tokens (this market) to the liquidator.
     * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another BToken.
     *  Its absolutely critical to use msg.sender as the seizer bToken and not a parameter.
     * @param seizerToken The contract seizing the collateral (i.e. borrowed bToken)
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of bTokens to seize
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint) {
        /* Fail if seize not allowed */
        uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens);
        if (allowed != 0) {
            return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed);
        }

        /* Fail if borrower = liquidator */
        if (borrower == liquidator) {
            return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER);
        }

        MathError mathErr;
        uint borrowerTokensNew;
        uint liquidatorTokensNew;

        /*
         * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
         *  borrowerTokensNew = accountTokens[borrower] - seizeTokens
         *  liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
         */
        (mathErr, borrowerTokensNew) = subUInt(accountTokens[borrower], seizeTokens);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, uint(mathErr));
        }

        (mathErr, liquidatorTokensNew) = addUInt(accountTokens[liquidator], seizeTokens);
        if (mathErr != MathError.NO_ERROR) {
            return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, uint(mathErr));
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We write the previously calculated values into storage */
        accountTokens[borrower] = borrowerTokensNew;
        accountTokens[liquidator] = liquidatorTokensNew;

        /* Emit a Transfer event */
        emit Transfer(borrower, liquidator, seizeTokens);

        /* We call the defense hook */
        comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens);

        return uint(Error.NO_ERROR);
    }


    /*** Admin Functions ***/

    /**
      * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
      * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
      * @param newPendingAdmin New pending admin.
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
        }

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return uint(Error.NO_ERROR);
    }

    /**
      * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
      * @dev Admin function for pending admin to accept role and update admin
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _acceptAdmin() external returns (uint) {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        if (msg.sender != pendingAdmin || msg.sender == address(0)) {
            return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK);
        }

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = address(0);

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);

        return uint(Error.NO_ERROR);
    }

    /**
      * @notice Sets a new comptroller for the market
      * @dev Admin function to set a new comptroller
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
        }

        ComptrollerInterface oldComptroller = comptroller;
        // Ensure invoke comptroller.isComptroller() returns true
        require(newComptroller.isComptroller(), "marker method returned false");

        // Set market's comptroller to newComptroller
        comptroller = newComptroller;

        // Emit NewComptroller(oldComptroller, newComptroller)
        emit NewComptroller(oldComptroller, newComptroller);

        return uint(Error.NO_ERROR);
    }

    /**
      * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
      * @dev Admin function to accrue interest and set a new reserve factor
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setReserveFactor(uint newReserveFactorMantissa) external nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed.
            return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED);
        }
        // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
        return _setReserveFactorFresh(newReserveFactorMantissa);
    }

    /**
      * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
      * @dev Admin function to set a new reserve factor
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK);
        }

        // Verify market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK);
        }

        // Check newReserveFactor ≤ maxReserveFactor
        if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
            return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK);
        }

        uint oldReserveFactorMantissa = reserveFactorMantissa;
        reserveFactorMantissa = newReserveFactorMantissa;

        emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Accrues interest and reduces reserves by transferring from msg.sender
     * @param addAmount Amount of addition to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
            return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED);
        }

        // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
        (error, ) = _addReservesFresh(addAmount);
        return error;
    }

    /**
     * @notice Add reserves by transferring from caller
     * @dev Requires fresh interest accrual
     * @param addAmount Amount of addition to reserves
     * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
     */
    function _addReservesFresh(uint addAmount) internal returns (uint, uint) {
        // totalReserves + actualAddAmount
        uint totalReservesNew;
        uint actualAddAmount;

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We call doTransferIn for the caller and the addAmount
         *  Note: The bToken must handle variations between BEP-20 and BNB underlying.
         *  On success, the bToken holds an additional addAmount of cash.
         *  doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
         *  it returns the amount actually transferred, in case of a fee.
         */

        actualAddAmount = doTransferIn(msg.sender, addAmount);

        totalReservesNew = totalReserves + actualAddAmount;

        /* Revert on overflow */
        require(totalReservesNew >= totalReserves, "add reserves unexpected overflow");

        // Store reserves[n+1] = reserves[n] + actualAddAmount
        totalReserves = totalReservesNew;

        /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */
        emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);

        /* Return (NO_ERROR, actualAddAmount) */
        return (uint(Error.NO_ERROR), actualAddAmount);
    }


    /**
     * @notice Accrues interest and reduces reserves by transferring to admin
     * @param reduceAmount Amount of reduction to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _reduceReserves(uint reduceAmount) external nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
            return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED);
        }
        // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
        return _reduceReservesFresh(reduceAmount);
    }

    /**
     * @notice Reduces reserves by transferring to admin
     * @dev Requires fresh interest accrual
     * @param reduceAmount Amount of reduction to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _reduceReservesFresh(uint reduceAmount) internal returns (uint) {
        // totalReserves - reduceAmount
        uint totalReservesNew;

        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK);
        }

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK);
        }

        // Fail gracefully if protocol has insufficient underlying cash
        if (getCashPrior() < reduceAmount) {
            return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_ABAILABLE);
        }

        // Check reduceAmount ≤ reserves[n] (totalReserves)
        if (reduceAmount > totalReserves) {
            return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION);
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        totalReservesNew = totalReserves - reduceAmount;
        // We checked reduceAmount <= totalReserves above, so this should never revert.
        require(totalReservesNew <= totalReserves, "reduce reserves unexpected underflow");

        // Store reserves[n+1] = reserves[n] - reduceAmount
        totalReserves = totalReservesNew;

        // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
        doTransferOut(admin, reduceAmount);

        emit ReservesReduced(admin, reduceAmount, totalReservesNew);

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
     * @dev Admin function to accrue interest and update the interest rate model
     * @param newInterestRateModel the new interest rate model to use
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed
            return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED);
        }
        // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
        return _setInterestRateModelFresh(newInterestRateModel);
    }

    /**
     * @notice updates the interest rate model (*requires fresh interest accrual)
     * @dev Admin function to update the interest rate model
     * @param newInterestRateModel the new interest rate model to use
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) {

        // Used to store old model for use in the event that is emitted on success
        InterestRateModel oldInterestRateModel;

        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK);
        }

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK);
        }

        // Track the market's current interest rate model
        oldInterestRateModel = interestRateModel;

        // Ensure invoke newInterestRateModel.isInterestRateModel() returns true
        require(newInterestRateModel.isInterestRateModel(), "marker method returned false");

        // Set the interest rate model to newInterestRateModel
        interestRateModel = newInterestRateModel;

        // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
        emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);

        return uint(Error.NO_ERROR);
    }

    /*** Safe Token ***/

    /**
     * @notice Gets balance of this contract in terms of the underlying
     * @dev This excludes the value of the current message, if any
     * @return The quantity of underlying owned by this contract
     */
    function getCashPrior() internal view returns (uint);

    /**
     * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
     *  This may revert due to insufficient balance or insufficient allowance.
     */
    function doTransferIn(address from, uint amount) internal returns (uint);

    /**
     * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting.
     *  If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
     *  If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
     */
    function doTransferOut(address payable to, uint amount) internal;


    /*** Reentrancy Guard ***/

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     */
    modifier nonReentrant() {
        require(_notEntered, "re-entered");
        _notEntered = false;
        _;
        _notEntered = true; // get a gas-refund post-Istanbul
    }
}

// File: contracts/PriceOracle.sol

pragma solidity ^0.5.16;


contract PriceOracle {
    /// @notice Indicator that this is a PriceOracle contract (for inspection)
    bool public constant isPriceOracle = true;

    /**
      * @notice Get the underlying price of a bToken asset
      * @param bToken The bToken to get the underlying price of
      * @return The underlying asset price mantissa (scaled by 1e18).
      *  Zero means the price is unavailable.
      */
    function getUnderlyingPrice(BToken bToken) external view returns (uint);
}

// File: contracts/BAIControllerStorage.sol

pragma solidity ^0.5.16;


contract BAIUnitrollerAdminStorage {
    /**
    * @notice Administrator for this contract
    */
    address public admin;

    /**
    * @notice Pending administrator for this contract
    */
    address public pendingAdmin;

    /**
    * @notice Active brains of Unitroller
    */
    address public baiControllerImplementation;

    /**
    * @notice Pending brains of Unitroller
    */
    address public pendingBAIControllerImplementation;
}

contract BAIControllerStorageG1 is BAIUnitrollerAdminStorage {
    ComptrollerInterface public comptroller;

    struct BidaoBAIState {
        /// @notice The last updated bidaoBAIMintIndex
        uint224 index;

        /// @notice The block number the index was last updated at
        uint32 block;
    }

    /// @notice The Bidao BAI state
    BidaoBAIState public bidaoBAIState;

    /// @notice The Bidao BAI state initialized
    bool public isBidaoBAIInitialized;

    /// @notice The Bidao BAI minter index as of the last time they accrued XBID
    mapping(address => uint) public bidaoBAIMinterIndex;
}

contract BAIControllerStorageG2 is BAIControllerStorageG1 {
    /// @notice Treasury Guardian address
    address public treasuryGuardian;

    /// @notice Treasury address
    address public treasuryAddress;

    /// @notice Fee percent of accrued interest with decimal 18
    uint256 public treasuryPercent;

    /// @notice Guard variable for re-entrancy checks
    bool internal _notEntered;
}

// File: contracts/BAIUnitroller.sol

pragma solidity ^0.5.16;



contract BAIUnitroller is BAIUnitrollerAdminStorage, BAIControllerErrorReporter {

    /**
      * @notice Emitted when pendingBAIControllerImplementation is changed
      */
    event NewPendingImplementation(address oldPendingImplementation, address newPendingImplementation);

    /**
      * @notice Emitted when pendingBAIControllerImplementation is accepted, which means comptroller implementation is updated
      */
    event NewImplementation(address oldImplementation, address newImplementation);

    /**
      * @notice Emitted when pendingAdmin is changed
      */
    event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

    /**
      * @notice Emitted when pendingAdmin is accepted, which means admin is updated
      */
    event NewAdmin(address oldAdmin, address newAdmin);

    constructor() public {
        // Set admin to caller
        admin = msg.sender;
    }

    /*** Admin Functions ***/
    function _setPendingImplementation(address newPendingImplementation) public returns (uint) {

        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK);
        }

        address oldPendingImplementation = pendingBAIControllerImplementation;

        pendingBAIControllerImplementation = newPendingImplementation;

        emit NewPendingImplementation(oldPendingImplementation, pendingBAIControllerImplementation);

        return uint(Error.NO_ERROR);
    }

    /**
    * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation
    * @dev Admin function for new implementation to accept it's role as implementation
    * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
    */
    function _acceptImplementation() public returns (uint) {
        // Check caller is pendingImplementation
        if (msg.sender != pendingBAIControllerImplementation) {
            return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK);
        }

        // Save current values for inclusion in log
        address oldImplementation = baiControllerImplementation;
        address oldPendingImplementation = pendingBAIControllerImplementation;

        baiControllerImplementation = pendingBAIControllerImplementation;

        pendingBAIControllerImplementation = address(0);

        emit NewImplementation(oldImplementation, baiControllerImplementation);
        emit NewPendingImplementation(oldPendingImplementation, pendingBAIControllerImplementation);

        return uint(Error.NO_ERROR);
    }


    /**
      * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
      * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
      * @param newPendingAdmin New pending admin.
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setPendingAdmin(address newPendingAdmin) public returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
        }

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return uint(Error.NO_ERROR);
    }

    /**
      * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
      * @dev Admin function for pending admin to accept role and update admin
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _acceptAdmin() public returns (uint) {
        // Check caller is pendingAdmin
        if (msg.sender != pendingAdmin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK);
        }

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = address(0);

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);

        return uint(Error.NO_ERROR);
    }

    /**
     * @dev Delegates execution to an implementation contract.
     * It returns to the external caller whatever the implementation returns
     * or forwards reverts.
     */
    function () external payable {
        // delegate all other functions to current implementation
        (bool success, ) = baiControllerImplementation.delegatecall(msg.data);

        assembly {
              let free_mem_ptr := mload(0x40)
              returndatacopy(free_mem_ptr, 0, returndatasize)

              switch success
              case 0 { revert(free_mem_ptr, returndatasize) }
              default { return(free_mem_ptr, returndatasize) }
        }
    }
}

// File: contracts/BAI/lib.sol

// SPDX-License-Identifier: AGPL-3.0-or-later

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.5.16;

contract LibNote {
    event LogNote(
        bytes4   indexed  sig,
        address  indexed  usr,
        bytes32  indexed  arg1,
        bytes32  indexed  arg2,
        bytes             data
    ) anonymous;

    modifier note {
        _;
        assembly {
            // log an 'anonymous' event with a constant 6 words of calldata
            // and four indexed topics: selector, caller, arg1 and arg2
            let mark := msize()                       // end of memory ensures zero
            mstore(0x40, add(mark, 288))              // update free memory pointer
            mstore(mark, 0x20)                        // bytes type data offset
            mstore(add(mark, 0x20), 224)              // bytes size (padded)
            calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
            log4(mark, 288,                           // calldata
                 shl(224, shr(224, calldataload(0))), // msg.sig
                 caller(),                            // msg.sender
                 calldataload(4),                     // arg1
                 calldataload(36)                     // arg2
                )
        }
    }
}

// File: contracts/BAI/BAI.sol

// SPDX-License-Identifier: AGPL-3.0-or-later

// Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.5.16;


contract BAI is LibNote {
    // --- Auth ---
    mapping (address => uint) public wards;
    function rely(address guy) external note auth { wards[guy] = 1; }
    function deny(address guy) external note auth { wards[guy] = 0; }
    modifier auth {
        require(wards[msg.sender] == 1, "BAI/not-authorized");
        _;
    }

    // --- BEP20 Data ---
    string  public constant name     = "BAI Stablecoin";
    string  public constant symbol   = "BAI";
    string  public constant version  = "1";
    uint8   public constant decimals = 18;
    uint256 public totalSupply;

    mapping (address => uint)                      public balanceOf;
    mapping (address => mapping (address => uint)) public allowance;
    mapping (address => uint)                      public nonces;

    event Approval(address indexed src, address indexed guy, uint wad);
    event Transfer(address indexed src, address indexed dst, uint wad);

    // --- Math ---
    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x, "BAI math error");
    }
    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x, "BAI math error");
    }

    // --- EIP712 niceties ---
    bytes32 public DOMAIN_SEPARATOR;
    // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
    bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;

    constructor(uint256 chainId_) public {
        wards[msg.sender] = 1;
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes(name)),
            keccak256(bytes(version)),
            chainId_,
            address(this)
        ));
    }

    // --- Token ---
    function transfer(address dst, uint wad) external returns (bool) {
        return transferFrom(msg.sender, dst, wad);
    }
    function transferFrom(address src, address dst, uint wad)
        public returns (bool)
    {
        require(balanceOf[src] >= wad, "BAI/insufficient-balance");
        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
            require(allowance[src][msg.sender] >= wad, "BAI/insufficient-allowance");
            allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
        }
        balanceOf[src] = sub(balanceOf[src], wad);
        balanceOf[dst] = add(balanceOf[dst], wad);
        emit Transfer(src, dst, wad);
        return true;
    }
    function mint(address usr, uint wad) external auth {
        balanceOf[usr] = add(balanceOf[usr], wad);
        totalSupply = add(totalSupply, wad);
        emit Transfer(address(0), usr, wad);
    }
    function burn(address usr, uint wad) external {
        require(balanceOf[usr] >= wad, "BAI/insufficient-balance");
        if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
            require(allowance[usr][msg.sender] >= wad, "BAI/insufficient-allowance");
            allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
        }
        balanceOf[usr] = sub(balanceOf[usr], wad);
        totalSupply = sub(totalSupply, wad);
        emit Transfer(usr, address(0), wad);
    }
    function approve(address usr, uint wad) external returns (bool) {
        allowance[msg.sender][usr] = wad;
        emit Approval(msg.sender, usr, wad);
        return true;
    }

    // --- Alias ---
    function push(address usr, uint wad) external {
        transferFrom(msg.sender, usr, wad);
    }
    function pull(address usr, uint wad) external {
        transferFrom(usr, msg.sender, wad);
    }
    function move(address src, address dst, uint wad) external {
        transferFrom(src, dst, wad);
    }

    // --- Approve by signature ---
    function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                    bool allowed, uint8 v, bytes32 r, bytes32 s) external
    {
        bytes32 digest = keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     allowed))
        ));

        require(holder != address(0), "BAI/invalid-address-0");
        require(holder == ecrecover(digest, v, r, s), "BAI/invalid-permit");
        require(expiry == 0 || now <= expiry, "BAI/permit-expired");
        require(nonce == nonces[holder]++, "BAI/invalid-nonce");
        uint wad = allowed ? uint(-1) : 0;
        allowance[holder][spender] = wad;
        emit Approval(holder, spender, wad);
    }
}

// File: contracts/BAIController.sol

pragma solidity ^0.5.16;








interface ComptrollerImplInterface {
    function protocolPaused() external view returns (bool);
    function mintedBAIs(address account) external view returns (uint);
    function vaiMintRate() external view returns (uint);
    function bidaoBAIRate() external view returns (uint);
    function bidaoAccrued(address account) external view returns(uint);
    function getAssetsIn(address account) external view returns (BToken[] memory);
    function oracle() external view returns (PriceOracle);

    function distributeBAIMinterBidao(address vaiMinter) external;
}

/**
 * @title Bidao's BAI Comptroller Contract
 * @author Bidao
 */
contract BAIController is BAIControllerStorageG2, BAIControllerErrorReporter, Exponential {

    /// @notice Emitted when Comptroller is changed
    event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller);

    /**
     * @notice Event emitted when BAI is minted
     */
    event MintBAI(address minter, uint mintBAIAmount);

    /**
     * @notice Event emitted when BAI is repaid
     */
    event RepayBAI(address payer, address borrower, uint repayBAIAmount);

    /// @notice The initial Bidao index for a market
    uint224 public constant bidaoInitialIndex = 1e36;

    /**
     * @notice Event emitted when a borrow is liquidated
     */
    event LiquidateBAI(address liquidator, address borrower, uint repayAmount, address bTokenCollateral, uint seizeTokens);

    /**
     * @notice Emitted when treasury guardian is changed
     */
    event NewTreasuryGuardian(address oldTreasuryGuardian, address newTreasuryGuardian);

    /**
     * @notice Emitted when treasury address is changed
     */
    event NewTreasuryAddress(address oldTreasuryAddress, address newTreasuryAddress);

    /**
     * @notice Emitted when treasury percent is changed
     */
    event NewTreasuryPercent(uint oldTreasuryPercent, uint newTreasuryPercent);

    /**
     * @notice Event emitted when BAIs are minted and fee are transferred
     */
    event MintFee(address minter, uint feeAmount);

    /*** Main Actions ***/
    struct MintLocalVars {
        Error err;
        MathError mathErr;
        uint mintAmount;
    }

    constructor() public {
        // Set admin to caller
        admin = msg.sender;
    }

    function mintBAI(uint mintBAIAmount) external nonReentrant returns (uint) {
        if(address(comptroller) != address(0)) {
            require(mintBAIAmount > 0, "mintBAIAmount cannt be zero");

            require(!ComptrollerImplInterface(address(comptroller)).protocolPaused(), "protocol is paused");

            MintLocalVars memory vars;

            address minter = msg.sender;

            // Keep the flywheel moving
            updateBidaoBAIMintIndex();
            ComptrollerImplInterface(address(comptroller)).distributeBAIMinterBidao(minter);

            uint oErr;
            MathError mErr;
            uint accountMintBAINew;
            uint accountMintableBAI;

            (oErr, accountMintableBAI) = getMintableBAI(minter);
            if (oErr != uint(Error.NO_ERROR)) {
                return uint(Error.REJECTION);
            }

            // check that user have sufficient mintableBAI balance
            if (mintBAIAmount > accountMintableBAI) {
                return fail(Error.REJECTION, FailureInfo.BAI_MINT_REJECTION);
            }

            (mErr, accountMintBAINew) = addUInt(ComptrollerImplInterface(address(comptroller)).mintedBAIs(minter), mintBAIAmount);
            require(mErr == MathError.NO_ERROR, "BAI_MINT_AMOUNT_CALCULATION_FAILED");
            uint error = comptroller.setMintedBAIOf(minter, accountMintBAINew);
            if (error != 0 ) {
                return error;
            }

            uint feeAmount;
            uint remainedAmount;
            vars.mintAmount = mintBAIAmount;
            if (treasuryPercent != 0) {
                (vars.mathErr, feeAmount) = mulUInt(vars.mintAmount, treasuryPercent);
                if (vars.mathErr != MathError.NO_ERROR) {
                    return failOpaque(Error.MATH_ERROR, FailureInfo.MINT_FEE_CALCULATION_FAILED, uint(vars.mathErr));
                }

                (vars.mathErr, feeAmount) = divUInt(feeAmount, 1e18);
                if (vars.mathErr != MathError.NO_ERROR) {
                    return failOpaque(Error.MATH_ERROR, FailureInfo.MINT_FEE_CALCULATION_FAILED, uint(vars.mathErr));
                }

                (vars.mathErr, remainedAmount) = subUInt(vars.mintAmount, feeAmount);
                if (vars.mathErr != MathError.NO_ERROR) {
                    return failOpaque(Error.MATH_ERROR, FailureInfo.MINT_FEE_CALCULATION_FAILED, uint(vars.mathErr));
                }

                BAI(getBAIAddress()).mint(treasuryAddress, feeAmount);

                emit MintFee(minter, feeAmount);
            } else {
                remainedAmount = vars.mintAmount;
            }

            BAI(getBAIAddress()).mint(minter, remainedAmount);

            emit MintBAI(minter, remainedAmount);

            return uint(Error.NO_ERROR);
        }
    }

    /**
     * @notice Repay BAI
     */
    function repayBAI(uint repayBAIAmount) external nonReentrant returns (uint, uint) {
        if(address(comptroller) != address(0)) {
            require(repayBAIAmount > 0, "repayBAIAmount cannt be zero");

            require(!ComptrollerImplInterface(address(comptroller)).protocolPaused(), "protocol is paused");

            address payer = msg.sender;

            updateBidaoBAIMintIndex();
            ComptrollerImplInterface(address(comptroller)).distributeBAIMinterBidao(payer);

            return repayBAIFresh(msg.sender, msg.sender, repayBAIAmount);
        }
    }

    /**
     * @notice Repay BAI Internal
     * @notice Borrowed BAIs are repaid by another user (possibly the borrower).
     * @param payer the account paying off the BAI
     * @param borrower the account with the debt being payed off
     * @param repayAmount the amount of BAI being returned
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function repayBAIFresh(address payer, address borrower, uint repayAmount) internal returns (uint, uint) {
        uint actualBurnAmount;

        uint vaiBalanceBorrower = ComptrollerImplInterface(address(comptroller)).mintedBAIs(borrower);

        if(vaiBalanceBorrower > repayAmount) {
            actualBurnAmount = repayAmount;
        } else {
            actualBurnAmount = vaiBalanceBorrower;
        }

        MathError mErr;
        uint accountBAINew;

        BAI(getBAIAddress()).burn(payer, actualBurnAmount);

        (mErr, accountBAINew) = subUInt(vaiBalanceBorrower, actualBurnAmount);
        require(mErr == MathError.NO_ERROR, "BAI_BURN_AMOUNT_CALCULATION_FAILED");

        uint error = comptroller.setMintedBAIOf(borrower, accountBAINew);
        if (error != 0) {
            return (error, 0);
        }
        emit RepayBAI(payer, borrower, actualBurnAmount);

        return (uint(Error.NO_ERROR), actualBurnAmount);
    }

    /**
     * @notice The sender liquidates the vai minters collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of vai to be liquidated
     * @param bTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
     */
    function liquidateBAI(address borrower, uint repayAmount, BTokenInterface bTokenCollateral) external nonReentrant returns (uint, uint) {
        require(!ComptrollerImplInterface(address(comptroller)).protocolPaused(), "protocol is paused");

        uint error = bTokenCollateral.accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            return (fail(Error(error), FailureInfo.BAI_LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0);
        }

        // liquidateBAIFresh emits borrow-specific logs on errors, so we don't need to
        return liquidateBAIFresh(msg.sender, borrower, repayAmount, bTokenCollateral);
    }

    /**
     * @notice The liquidator liquidates the borrowers collateral by repay borrowers BAI.
     *  The collateral seized is transferred to the liquidator.
     * @param liquidator The address repaying the BAI and seizing collateral
     * @param borrower The borrower of this BAI to be liquidated
     * @param bTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the BAI to repay
     * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment BAI.
     */
    function liquidateBAIFresh(address liquidator, address borrower, uint repayAmount, BTokenInterface bTokenCollateral) internal returns (uint, uint) {
        if(address(comptroller) != address(0)) {
            /* Fail if liquidate not allowed */
            uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(bTokenCollateral), liquidator, borrower, repayAmount);
            if (allowed != 0) {
                return (failOpaque(Error.REJECTION, FailureInfo.BAI_LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0);
            }

            /* Verify bTokenCollateral market's block number equals current block number */
            //if (bTokenCollateral.accrualBlockNumber() != accrualBlockNumber) {
            if (bTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
                return (fail(Error.REJECTION, FailureInfo.BAI_LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0);
            }

            /* Fail if borrower = liquidator */
            if (borrower == liquidator) {
                return (fail(Error.REJECTION, FailureInfo.BAI_LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0);
            }

            /* Fail if repayAmount = 0 */
            if (repayAmount == 0) {
                return (fail(Error.REJECTION, FailureInfo.BAI_LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0);
            }

            /* Fail if repayAmount = -1 */
            if (repayAmount == uint(-1)) {
                return (fail(Error.REJECTION, FailureInfo.BAI_LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0);
            }


            /* Fail if repayBAI fails */
            (uint repayBorrowError, uint actualRepayAmount) = repayBAIFresh(liquidator, borrower, repayAmount);
            if (repayBorrowError != uint(Error.NO_ERROR)) {
                return (fail(Error(repayBorrowError), FailureInfo.BAI_LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0);
            }

            /////////////////////////
            // EFFECTS & INTERACTIONS
            // (No safe failures beyond this point)

            /* We calculate the number of collateral tokens that will be seized */
            (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateBAICalculateSeizeTokens(address(bTokenCollateral), actualRepayAmount);
            require(amountSeizeError == uint(Error.NO_ERROR), "BAI_LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");

            /* Revert if borrower collateral token balance < seizeTokens */
            require(bTokenCollateral.balanceOf(borrower) >= seizeTokens, "BAI_LIQUIDATE_SEIZE_TOO_MUCH");

            uint seizeError;
            seizeError = bTokenCollateral.seize(liquidator, borrower, seizeTokens);

            /* Revert if seize tokens fails (since we cannot be sure of side effects) */
            require(seizeError == uint(Error.NO_ERROR), "token seizure failed");

            /* We emit a LiquidateBorrow event */
            emit LiquidateBAI(liquidator, borrower, actualRepayAmount, address(bTokenCollateral), seizeTokens);

            /* We call the defense hook */
            comptroller.liquidateBorrowVerify(address(this), address(bTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens);

            return (uint(Error.NO_ERROR), actualRepayAmount);
        }
    }

    /**
     * @notice Initialize the BidaoBAIState
     */
    function _initializeBidaoBAIState(uint blockNumber) external returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
        }

        if (isBidaoBAIInitialized == false) {
            isBidaoBAIInitialized = true;
            uint vaiBlockNumber = blockNumber == 0 ? getBlockNumber() : blockNumber;
            bidaoBAIState = BidaoBAIState({
                index: bidaoInitialIndex,
                block: safe32(vaiBlockNumber, "block number overflows")
            });
        }

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Accrue XBID to by updating the BAI minter index
     */
    function updateBidaoBAIMintIndex() public returns (uint) {
        uint vaiMinterSpeed = ComptrollerImplInterface(address(comptroller)).bidaoBAIRate();
        uint blockNumber = getBlockNumber();
        uint deltaBlocks = sub_(blockNumber, uint(bidaoBAIState.block));
        if (deltaBlocks > 0 && vaiMinterSpeed > 0) {
            uint vaiAmount = BAI(getBAIAddress()).totalSupply();
            uint bidaoAccrued = mul_(deltaBlocks, vaiMinterSpeed);
            Double memory ratio = vaiAmount > 0 ? fraction(bidaoAccrued, vaiAmount) : Double({mantissa: 0});
            Double memory index = add_(Double({mantissa: bidaoBAIState.index}), ratio);
            bidaoBAIState = BidaoBAIState({
                index: safe224(index.mantissa, "new index overflows"),
                block: safe32(blockNumber, "block number overflows")
            });
        } else if (deltaBlocks > 0) {
            bidaoBAIState.block = safe32(blockNumber, "block number overflows");
        }

        return uint(Error.NO_ERROR);
    }

    /**
     * @notice Calculate XBID accrued by a BAI minter
     * @param vaiMinter The address of the BAI minter to distribute XBID to
     */
    function calcDistributeBAIMinterBidao(address vaiMinter) public returns(uint, uint, uint, uint) {
        // Check caller is comptroller
        if (msg.sender != address(comptroller)) {
            return (fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK), 0, 0, 0);
        }

        Double memory vaiMintIndex = Double({mantissa: bidaoBAIState.index});
        Double memory vaiMinterIndex = Double({mantissa: bidaoBAIMinterIndex[vaiMinter]});
        bidaoBAIMinterIndex[vaiMinter] = vaiMintIndex.mantissa;

        if (vaiMinterIndex.mantissa == 0 && vaiMintIndex.mantissa > 0) {
            vaiMinterIndex.mantissa = bidaoInitialIndex;
        }

        Double memory deltaIndex = sub_(vaiMintIndex, vaiMinterIndex);
        uint vaiMinterAmount = ComptrollerImplInterface(address(comptroller)).mintedBAIs(vaiMinter);
        uint vaiMinterDelta = mul_(vaiMinterAmount, deltaIndex);
        uint vaiMinterAccrued = add_(ComptrollerImplInterface(address(comptroller)).bidaoAccrued(vaiMinter), vaiMinterDelta);
        return (uint(Error.NO_ERROR), vaiMinterAccrued, vaiMinterDelta, vaiMintIndex.mantissa);
    }

    /*** Admin Functions ***/

    /**
      * @notice Sets a new comptroller
      * @dev Admin function to set a new comptroller
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _setComptroller(ComptrollerInterface comptroller_) external returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
        }

        ComptrollerInterface oldComptroller = comptroller;
        comptroller = comptroller_;
        emit NewComptroller(oldComptroller, comptroller_);

        return uint(Error.NO_ERROR);
    }

    function _become(BAIUnitroller unitroller) external {
        require(msg.sender == unitroller.admin(), "only unitroller admin can change brains");
        require(unitroller._acceptImplementation() == 0, "change not authorized");
    }

    /**
     * @dev Local vars for avoiding stack-depth limits in calculating account total supply balance.
     *  Note that `bTokenBalance` is the number of bTokens the account owns in the market,
     *  whereas `borrowBalance` is the amount of underlying that the account has borrowed.
     */
    struct AccountAmountLocalVars {
        uint totalSupplyAmount;
        uint sumSupply;
        uint sumBorrowPlusEffects;
        uint bTokenBalance;
        uint borrowBalance;
        uint exchangeRateMantissa;
        uint oraclePriceMantissa;
        Exp collateralFactor;
        Exp exchangeRate;
        Exp oraclePrice;
        Exp tokensToDenom;
    }

    function getMintableBAI(address minter) public view returns (uint, uint) {
        PriceOracle oracle = ComptrollerImplInterface(address(comptroller)).oracle();
        BToken[] memory enteredMarkets = ComptrollerImplInterface(address(comptroller)).getAssetsIn(minter);

        AccountAmountLocalVars memory vars; // Holds all our calculation results

        uint oErr;
        MathError mErr;

        uint accountMintableBAI;
        uint i;

        /**
         * We use this formula to calculate mintable BAI amount.
         * totalSupplyAmount * BAIMintRate - (totalBorrowAmount + mintedBAIOf)
         */
        for (i = 0; i < enteredMarkets.length; i++) {
            (oErr, vars.bTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = enteredMarkets[i].getAccountSnapshot(minter);
            if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades
                return (uint(Error.SNAPSHOT_ERROR), 0);
            }
            vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa});

            // Get the normalized price of the asset
            vars.oraclePriceMantissa = oracle.getUnderlyingPrice(enteredMarkets[i]);
            if (vars.oraclePriceMantissa == 0) {
                return (uint(Error.PRICE_ERROR), 0);
            }
            vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa});

            (mErr, vars.tokensToDenom) = mulExp(vars.exchangeRate, vars.oraclePrice);
            if (mErr != MathError.NO_ERROR) {
                return (uint(Error.MATH_ERROR), 0);
            }

            // sumSupply += tokensToDenom * bTokenBalance
            (mErr, vars.sumSupply) = mulScalarTruncateAddUInt(vars.tokensToDenom, vars.bTokenBalance, vars.sumSupply);
            if (mErr != MathError.NO_ERROR) {
                return (uint(Error.MATH_ERROR), 0);
            }

            // sumBorrowPlusEffects += oraclePrice * borrowBalance
            (mErr, vars.sumBorrowPlusEffects) = mulScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects);
            if (mErr != MathError.NO_ERROR) {
                return (uint(Error.MATH_ERROR), 0);
            }
        }

        (mErr, vars.sumBorrowPlusEffects) = addUInt(vars.sumBorrowPlusEffects, ComptrollerImplInterface(address(comptroller)).mintedBAIs(minter));
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.MATH_ERROR), 0);
        }

        (mErr, accountMintableBAI) = mulUInt(vars.sumSupply, ComptrollerImplInterface(address(comptroller)).vaiMintRate());
        require(mErr == MathError.NO_ERROR, "BAI_MINT_AMOUNT_CALCULATION_FAILED");

        (mErr, accountMintableBAI) = divUInt(accountMintableBAI, 10000);
        require(mErr == MathError.NO_ERROR, "BAI_MINT_AMOUNT_CALCULATION_FAILED");


        (mErr, accountMintableBAI) = subUInt(accountMintableBAI, vars.sumBorrowPlusEffects);
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.REJECTION), 0);
        }

        return (uint(Error.NO_ERROR), accountMintableBAI);
    }

    function _setTreasuryData(address newTreasuryGuardian, address newTreasuryAddress, uint newTreasuryPercent) external returns (uint) {
        // Check caller is admin
        if (!(msg.sender == admin || msg.sender == treasuryGuardian)) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_TREASURY_OWNER_CHECK);
        }

        require(newTreasuryPercent < 1e18, "treasury percent cap overflow");

        address oldTreasuryGuardian = treasuryGuardian;
        address oldTreasuryAddress = treasuryAddress;
        uint oldTreasuryPercent = treasuryPercent;

        treasuryGuardian = newTreasuryGuardian;
        treasuryAddress = newTreasuryAddress;
        treasuryPercent = newTreasuryPercent;

        emit NewTreasuryGuardian(oldTreasuryGuardian, newTreasuryGuardian);
        emit NewTreasuryAddress(oldTreasuryAddress, newTreasuryAddress);
        emit NewTreasuryPercent(oldTreasuryPercent, newTreasuryPercent);

        return uint(Error.NO_ERROR);
    }

    function getBlockNumber() public view returns (uint) {
        return block.number;
    }

    /**
     * @notice Return the address of the BAI token
     * @return The address of BAI
     */
    function getBAIAddress() public view returns (address) {
        return 0x773F4515463d189500419aebe5e6932A79E43dE6;
    }

    function initialize() onlyAdmin public {
        // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
        _notEntered = true;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin, "only admin can");
        _;
    }

    /*** Reentrancy Guard ***/

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     */
    modifier nonReentrant() {
        require(_notEntered, "re-entered");
        _notEntered = false;
        _;
        _notEntered = true; // get a gas-refund post-Istanbul
    }
}

Contract ABI

[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"bTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBAI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintBAIAmount","type":"uint256"}],"name":"MintBAI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"MintFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTreasuryAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasuryAddress","type":"address"}],"name":"NewTreasuryAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTreasuryGuardian","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasuryGuardian","type":"address"}],"name":"NewTreasuryGuardian","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldTreasuryPercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTreasuryPercent","type":"uint256"}],"name":"NewTreasuryPercent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayBAIAmount","type":"uint256"}],"name":"RepayBAI","type":"event"},{"constant":false,"inputs":[{"internalType":"contract BAIUnitroller","name":"unitroller","type":"address"}],"name":"_become","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"_initializeBidaoBAIState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newTreasuryGuardian","type":"address"},{"internalType":"address","name":"newTreasuryAddress","type":"address"},{"internalType":"uint256","name":"newTreasuryPercent","type":"uint256"}],"name":"_setTreasuryData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"baiControllerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bidaoBAIMinterIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bidaoBAIState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"block","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bidaoInitialIndex","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"vaiMinter","type":"address"}],"name":"calcDistributeBAIMinterBidao","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBAIAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"getMintableBAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isBidaoBAIInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract BTokenInterface","name":"bTokenCollateral","type":"address"}],"name":"liquidateBAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"mintBAIAmount","type":"uint256"}],"name":"mintBAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingBAIControllerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"repayBAIAmount","type":"uint256"}],"name":"repayBAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"treasuryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"treasuryGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"treasuryPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"updateBidaoBAIMintIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600080546001600160a01b03191633179055613042806100326000396000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c80639a76a05b116100de578063d34d2ba711610097578063ea23669f11610071578063ea23669f146103de578063ec386c1514610404578063ee2dccd914610428578063f851a4401461043057610173565b8063d34d2ba714610364578063da1d1621146103b0578063e555f510146103b857610173565b80639a76a05b146102c3578063b2eafc39146102e0578063b52e4f08146102e8578063b8a192631461031e578063c5f956af14610326578063d24febad1461032e57610173565b806345eaeef91161013057806345eaeef91461023c5780634f47b604146102595780635769247d1461028f5780635aa06088146102975780635fe3b567146102b35780638129fc1c146102bb57610173565b806304dc9e9a1461017857806304ef9d58146101a85780631d504dc6146101c257806326782247146101ea57806342cbb15c1461020e5780634576b5db14610216575b600080fd5b610180610438565b604080516001600160e01b03909316835263ffffffff90911660208301528051918290030190f35b6101b0610456565b60408051918252519081900360200190f35b6101e8600480360360208110156101d857600080fd5b50356001600160a01b031661045c565b005b6101f26105bb565b604080516001600160a01b039092168252519081900360200190f35b6101b06105ca565b6101b06004803603602081101561022c57600080fd5b50356001600160a01b03166105ce565b6101b06004803603602081101561025257600080fd5b503561065e565b6102766004803603602081101561026f57600080fd5b5035610759565b6040805192835260208301919091528051918290030190f35b6101f2610960565b61029f61096f565b604080519115158252519081900360200190f35b6101f2610978565b6101e8610987565b6101b0600480360360208110156102d957600080fd5b50356109e6565b6101f261106c565b610276600480360360608110156102fe57600080fd5b506001600160a01b0381358116916020810135916040909101351661107b565b6101b0611240565b6101f2611513565b6101b06004803603606081101561034457600080fd5b506001600160a01b03813581169160208101359091169060400135611522565b61038a6004803603602081101561037a57600080fd5b50356001600160a01b03166116c5565b604080519485526020850193909352838301919091526060830152519081900360800190f35b6101f26118fc565b6101b0600480360360208110156103ce57600080fd5b50356001600160a01b0316611914565b610276600480360360208110156103f457600080fd5b50356001600160a01b0316611926565b61040c611f55565b604080516001600160e01b039092168252519081900360200190f35b6101f2611f68565b6101f2611f77565b6005546001600160e01b03811690600160e01b900463ffffffff1682565b600a5481565b806001600160a01b031663f851a4406040518163ffffffff1660e01b815260040160206040518083038186803b15801561049557600080fd5b505afa1580156104a9573d6000803e3d6000fd5b505050506040513d60208110156104bf57600080fd5b50516001600160a01b031633146105075760405162461bcd60e51b8152600401808060200182810382526027815260200180612fe76027913960400191505060405180910390fd5b806001600160a01b031663c1e803346040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561054257600080fd5b505af1158015610556573d6000803e3d6000fd5b505050506040513d602081101561056c57600080fd5b5051156105b8576040805162461bcd60e51b815260206004820152601560248201527418da185b99d9481b9bdd08185d5d1a1bdc9a5e9959605a1b604482015290519081900360640190fd5b50565b6001546001600160a01b031681565b4390565b600080546001600160a01b031633146105f4576105ed60016002611f86565b9050610659565b600480546001600160a01b038481166001600160a01b0319831681179093556040805191909216808252602082019390935281517f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d929181900390910190a160009150505b919050565b600080546001600160a01b0316331461067d576105ed60016002611f86565b60065460ff16610751576006805460ff19166001179055600082156106a257826106aa565b6106aa6105ca565b905060405180604001604052806ec097ce7bc90715b34b9f10000000006001600160e01b0316815260200161070d8360405180604001604052806016815260200175626c6f636b206e756d626572206f766572666c6f777360501b815250611fec565b63ffffffff908116909152815160058054602090940151909216600160e01b026001600160e01b039182166001600160e01b03199094169390931716919091179055505b600092915050565b600b54600090819060ff166107a2576040805162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b604482015290519081900360640190fd5b600b805460ff191690556004546001600160a01b03161561094c5760008311610812576040805162461bcd60e51b815260206004820152601c60248201527f7265706179424149416d6f756e742063616e6e74206265207a65726f00000000604482015290519081900360640190fd5b600480546040805163084bf5ab60e31b815290516001600160a01b039092169263425fad58928282019260209290829003018186803b15801561085457600080fd5b505afa158015610868573d6000803e3d6000fd5b505050506040513d602081101561087e57600080fd5b5051156108c7576040805162461bcd60e51b81526020600482015260126024820152711c1c9bdd1bd8dbdb081a5cc81c185d5cd95960721b604482015290519081900360640190fd5b336108d0611240565b506004805460408051630930539160e41b81526001600160a01b03858116948201949094529051929091169163930539109160248082019260009290919082900301818387803b15801561092357600080fd5b505af1158015610937573d6000803e3d6000fd5b50505050610946333386612087565b92509250505b600b805460ff191660011790559092909150565b6002546001600160a01b031681565b60065460ff1681565b6004546001600160a01b031681565b6000546001600160a01b031633146109d7576040805162461bcd60e51b815260206004820152600e60248201526d37b7363c9030b236b4b71031b0b760911b604482015290519081900360640190fd5b600b805460ff19166001179055565b600b5460009060ff16610a2d576040805162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b604482015290519081900360640190fd5b600b805460ff191690556004546001600160a01b03161561105a5760008211610a9d576040805162461bcd60e51b815260206004820152601b60248201527f6d696e74424149416d6f756e742063616e6e74206265207a65726f0000000000604482015290519081900360640190fd5b600480546040805163084bf5ab60e31b815290516001600160a01b039092169263425fad58928282019260209290829003018186803b158015610adf57600080fd5b505afa158015610af3573d6000803e3d6000fd5b505050506040513d6020811015610b0957600080fd5b505115610b52576040805162461bcd60e51b81526020600482015260126024820152711c1c9bdd1bd8dbdb081a5cc81c185d5cd95960721b604482015290519081900360640190fd5b610b5a612ec5565b33610b63611240565b506004805460408051630930539160e41b81526001600160a01b03858116948201949094529051929091169163930539109160248082019260009290919082900301818387803b158015610bb657600080fd5b505af1158015610bca573d6000803e3d6000fd5b50505050600080600080610bdd85611926565b90945090508315610bf85760025b965050505050505061105a565b80881115610c0c57610beb60026005611f86565b6004805460408051633d67eb2160e01b81526001600160a01b03898116948201949094529051610c929390921691633d67eb2191602480820192602092909190829003018186803b158015610c6057600080fd5b505afa158015610c74573d6000803e3d6000fd5b505050506040513d6020811015610c8a57600080fd5b505189612312565b90935091506000836003811115610ca557fe5b14610ce15760405162461bcd60e51b8152600401808060200182810382526022815260200180612fc56022913960400191505060405180910390fd5b6004805460408051631ca2aaff60e11b81526001600160a01b03898116948201949094526024810186905290516000939092169163394555fe9160448082019260209290919082900301818787803b158015610d3c57600080fd5b505af1158015610d50573d6000803e3d6000fd5b505050506040513d6020811015610d6657600080fd5b505190508015610d7e57965061105a95505050505050565b60408701899052600a54600090819015610f8957610da28960400151600a5461233b565b8a602001819450826003811115610db557fe5b6003811115610dc057fe5b9052506000905089602001516003811115610dd757fe5b14610e0857610df8600560168b602001516003811115610df357fe5b61237a565b995050505050505050505061105a565b610e1a82670de0b6b3a76400006123e8565b8a602001819450826003811115610e2d57fe5b6003811115610e3857fe5b9052506000905089602001516003811115610e4f57fe5b14610e6b57610df8600560168b602001516003811115610df357fe5b610e79896040015183612413565b8a602001819350826003811115610e8c57fe5b6003811115610e9757fe5b9052506000905089602001516003811115610eae57fe5b14610eca57610df8600560168b602001516003811115610df357fe5b610ed26118fc565b600954604080516340c10f1960e01b81526001600160a01b03928316600482015260248101869052905192909116916340c10f199160448082019260009290919082900301818387803b158015610f2857600080fd5b505af1158015610f3c573d6000803e3d6000fd5b5050604080516001600160a01b038c1681526020810186905281517fb0715a6d41a37c1b0672c22c09a31a0642c1fb3f9efa2d5fd5c6d2d891ee78c69450908190039091019150a1610f90565b5060408801515b610f986118fc565b6001600160a01b03166340c10f1989836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015610ff757600080fd5b505af115801561100b573d6000803e3d6000fd5b5050604080516001600160a01b038c1681526020810185905281517f5335f2c08452e5a6476a4e546ddf6edf111fd4f94a3b97cd710426bea7373f4a9450908190039091019150a16000610df8565b600b805460ff19166001179055919050565b6008546001600160a01b031681565b600b54600090819060ff166110c4576040805162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b604482015290519081900360640190fd5b600b805460ff19169055600480546040805163084bf5ab60e31b815290516001600160a01b039092169263425fad58928282019260209290829003018186803b15801561111057600080fd5b505afa158015611124573d6000803e3d6000fd5b505050506040513d602081101561113a57600080fd5b505115611183576040805162461bcd60e51b81526020600482015260126024820152711c1c9bdd1bd8dbdb081a5cc81c185d5cd95960721b604482015290519081900360640190fd5b6000836001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111c057600080fd5b505af11580156111d4573d6000803e3d6000fd5b505050506040513d60208110156111ea57600080fd5b5051905080156112175761120a81600681111561120357fe5b6008611f86565b9250600091506112299050565b61122333878787612436565b92509250505b600b805460ff191660011790559094909350915050565b600480546040805163f6643c4360e01b8152905160009384936001600160a01b03169263f6643c439281830192602092829003018186803b15801561128457600080fd5b505afa158015611298573d6000803e3d6000fd5b505050506040513d60208110156112ae57600080fd5b5051905060006112bc6105ca565b6005549091506000906112dd908390600160e01b900463ffffffff16612989565b90506000811180156112ef5750600083115b156114a65760006112fe6118fc565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561133657600080fd5b505afa15801561134a573d6000803e3d6000fd5b505050506040513d602081101561136057600080fd5b50519050600061137083866129c3565b905061137a612ee7565b6000831161139757604051806020016040528060008152506113a1565b6113a18284612a05565b90506113ab612ee7565b60408051602081019091526005546001600160e01b031681526113ce9083612a43565b905060405180604001604052806114148360000151604051806040016040528060138152602001726e657720696e646578206f766572666c6f777360681b815250612a68565b6001600160e01b0316815260200161145a8860405180604001604052806016815260200175626c6f636b206e756d626572206f766572666c6f777360501b815250611fec565b63ffffffff908116909152815160058054602090940151909216600160e01b026001600160e01b039182166001600160e01b031990941693909317169190911790555061150992505050565b8015611509576114e48260405180604001604052806016815260200175626c6f636b206e756d626572206f766572666c6f777360501b815250611fec565b6005805463ffffffff92909216600160e01b026001600160e01b039092169190911790555b6000935050505090565b6009546001600160a01b031681565b600080546001600160a01b031633148061154657506008546001600160a01b031633145b61155d5761155660016017611f86565b90506116be565b670de0b6b3a764000082106115b9576040805162461bcd60e51b815260206004820152601d60248201527f74726561737572792070657263656e7420636170206f766572666c6f77000000604482015290519081900360640190fd5b6008805460098054600a80546001600160a01b038a81166001600160a01b031980881682179098558a821697851697909717909455908790556040805194841680865260208601969096528051929093169390927f29f06ea15931797ebaed313d81d100963dc22cb213cb4ce2737b5a62b1a8b1e892918290030190a1604080516001600160a01b0380851682528816602082015281517f8de763046d7b8f08b6c3d03543de1d615309417842bb5d2d62f110f65809ddac929181900390910190a1604080518281526020810187905281517f0893f8f4101baaabbeb513f96761e7a36eb837403c82cc651c292a4abdc94ed7929181900390910190a1600093505050505b9392505050565b6004546000908190819081906001600160a01b031633146116fd576116ec60016002611f86565b9350600092508291508190506118f5565b611705612ee7565b5060408051602081019091526005546001600160e01b03168152611727612ee7565b5060408051602080820183526001600160a01b038916600081815260078084529481208054855286519290915293909152909155805115801561176a5750815115155b15611782576ec097ce7bc90715b34b9f100000000081525b61178a612ee7565b6117948383612abd565b90506000600460009054906101000a90046001600160a01b03166001600160a01b0316633d67eb218a6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561180357600080fd5b505afa158015611817573d6000803e3d6000fd5b505050506040513d602081101561182d57600080fd5b50519050600061183d8284612ae2565b905060006118e1600460009054906101000a90046001600160a01b03166001600160a01b031663c0342f768d6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156118af57600080fd5b505afa1580156118c3573d6000803e3d6000fd5b505050506040513d60208110156118d957600080fd5b505183612b11565b905060009551959950975095509293505050505b9193509193565b73773f4515463d189500419aebe5e6932a79e43de690565b60076020526000908152604090205481565b6000806000600460009054906101000a90046001600160a01b03166001600160a01b0316637dc0d1d06040518163ffffffff1660e01b815260040160206040518083038186803b15801561197957600080fd5b505afa15801561198d573d6000803e3d6000fd5b505050506040513d60208110156119a357600080fd5b50516004805460408051632aff3bff60e21b81526001600160a01b03898116948201949094529051939450606093929091169163abfceffc91602480820192600092909190829003018186803b1580156119fc57600080fd5b505afa158015611a10573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015611a3957600080fd5b8101908080516040519392919084640100000000821115611a5957600080fd5b908301906020820185811115611a6e57600080fd5b8251866020820283011164010000000082111715611a8b57600080fd5b82525081516020918201928201910280838360005b83811015611ab8578181015183820152602001611aa0565b505050509050016040525050509050611acf612efa565b60008080805b8551811015611d3b57858181518110611aea57fe5b60200260200101516001600160a01b031663c37f68e28b6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060806040518083038186803b158015611b4757600080fd5b505afa158015611b5b573d6000803e3d6000fd5b505050506040513d6080811015611b7157600080fd5b5080516020820151604083015160609384015160a08a015260808901529187019190915293508315611bb35760035b985060009750611f509650505050505050565b60405180602001604052808660a00151815250856101000181905250866001600160a01b031663fc57d4df878381518110611bea57fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611c3857600080fd5b505afa158015611c4c573d6000803e3d6000fd5b505050506040513d6020811015611c6257600080fd5b505160c08601819052611c76576004611ba0565b604080516020810190915260c086015181526101208601819052610100860151611c9f91612b47565b61014087015292506000836003811115611cb557fe5b14611cc1576005611ba0565b611cd985610140015186606001518760200151612c30565b602087015292506000836003811115611cee57fe5b14611cfa576005611ba0565b611d1285610120015186608001518760400151612c30565b604087015292506000836003811115611d2757fe5b14611d33576005611ba0565b600101611ad5565b604080860151600480548351633d67eb2160e01b81526001600160a01b038f8116938201939093529351611dc4949290911691633d67eb21916024808301926020929190829003018186803b158015611d9357600080fd5b505afa158015611da7573d6000803e3d6000fd5b505050506040513d6020811015611dbd57600080fd5b5051612312565b604087015292506000836003811115611dd957fe5b14611de5576005611ba0565b611e6c8560200151600460009054906101000a90046001600160a01b03166001600160a01b031663bec04f726040518163ffffffff1660e01b815260040160206040518083038186803b158015611e3b57600080fd5b505afa158015611e4f573d6000803e3d6000fd5b505050506040513d6020811015611e6557600080fd5b505161233b565b90935091506000836003811115611e7f57fe5b14611ebb5760405162461bcd60e51b8152600401808060200182810382526022815260200180612fc56022913960400191505060405180910390fd5b611ec7826127106123e8565b90935091506000836003811115611eda57fe5b14611f165760405162461bcd60e51b8152600401808060200182810382526022815260200180612fc56022913960400191505060405180910390fd5b611f24828660400151612413565b90935091506000836003811115611f3757fe5b14611f43576002611ba0565b5060009750955050505050505b915091565b6ec097ce7bc90715b34b9f100000000081565b6003546001600160a01b031681565b6000546001600160a01b031681565b60007f45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa0836006811115611fb557fe5b836017811115611fc157fe5b604080519283526020830191909152600082820152519081900360600190a18260068111156116be57fe5b600081640100000000841061207f5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561204457818101518382015260200161202c565b50505050905090810190601f1680156120715780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b509192915050565b600080600080600460009054906101000a90046001600160a01b03166001600160a01b0316633d67eb21876040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156120f857600080fd5b505afa15801561210c573d6000803e3d6000fd5b505050506040513d602081101561212257600080fd5b50519050848111156121365784915061213a565b8091505b6000806121456118fc565b6001600160a01b0316639dc29fac8a866040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156121a457600080fd5b505af11580156121b8573d6000803e3d6000fd5b505050506121c68385612413565b909250905060008260038111156121d957fe5b146122155760405162461bcd60e51b8152600401808060200182810382526022815260200180612f6c6022913960400191505060405180910390fd5b6004805460408051631ca2aaff60e11b81526001600160a01b038c8116948201949094526024810185905290516000939092169163394555fe9160448082019260209290919082900301818787803b15801561227057600080fd5b505af1158015612284573d6000803e3d6000fd5b505050506040513d602081101561229a57600080fd5b5051905080156122b45795506000945061230a9350505050565b604080516001600160a01b03808d1682528b16602082015280820187905290517f7d214b8d9319d1bb0f07704751014ed965837e77eaae058c3013d89a7b39a2049181900360600190a160009650939450505050505b935093915050565b60008083830184811061232a57600092509050612334565b5060029150600090505b9250929050565b6000808361234e57506000905080612334565b8383028385828161235b57fe5b041461236f57506002915060009050612334565b600092509050612334565b60007f45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa08460068111156123a957fe5b8460178111156123b557fe5b604080519283526020830191909152818101859052519081900360600190a18360068111156123e057fe5b949350505050565b600080826123fc5750600190506000612334565b600083858161240757fe5b04915091509250929050565b60008083831161242a575060009050818303612334565b50600390506000612334565b60045460009081906001600160a01b031615612980576004805460408051632fe3f38f60e11b815230938101939093526001600160a01b038681166024850152898116604485015288811660648501526084840188905290516000939190921691635fc7e71e9160a48082019260209290919082900301818787803b1580156124be57600080fd5b505af11580156124d2573d6000803e3d6000fd5b505050506040513d60208110156124e857600080fd5b50519050801561250c576124ff6002600a8361237a565b9250600091506129809050565b6125146105ca565b846001600160a01b0316636c540baf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561254d57600080fd5b505afa158015612561573d6000803e3d6000fd5b505050506040513d602081101561257757600080fd5b50511461258a576124ff60026009611f86565b866001600160a01b0316866001600160a01b031614156125b0576124ff6002600f611f86565b846125c1576124ff6002600d611f86565b6000198514156125d7576124ff6002600c611f86565b6000806125e5898989612087565b90925090508115612615576126068260068111156125ff57fe5b6010611f86565b94506000935061298092505050565b60048054604080516301b1015560e21b81526001600160a01b038a811694820194909452602481018590528151600094859416926306c405549260448082019391829003018186803b15801561266a57600080fd5b505afa15801561267e573d6000803e3d6000fd5b505050506040513d604081101561269457600080fd5b508051602090910151909250905081156126df5760405162461bcd60e51b8152600401808060200182810382526037815260200180612f8e6037913960400191505060405180910390fd5b80886001600160a01b03166370a082318c6040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561273657600080fd5b505afa15801561274a573d6000803e3d6000fd5b505050506040513d602081101561276057600080fd5b505110156127b5576040805162461bcd60e51b815260206004820152601c60248201527f4241495f4c49515549444154455f5345495a455f544f4f5f4d55434800000000604482015290519081900360640190fd5b6040805163b2a02ff160e01b81526001600160a01b038d811660048301528c811660248301526044820184905291516000928b169163b2a02ff191606480830192602092919082900301818787803b15801561281057600080fd5b505af1158015612824573d6000803e3d6000fd5b505050506040513d602081101561283a57600080fd5b505190508015612888576040805162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b604482015290519081900360640190fd5b604080516001600160a01b03808f168252808e1660208301528183018790528b1660608201526080810184905290517f83ecdfdced5e34d6a5299bf26409d6b15fdef659d09455a3b5339ff9cb91f6ad9181900360a00190a160048054604080516347ef3b3b60e01b815230938101939093526001600160a01b038c811660248501528f811660448501528e811660648501526084840188905260a4840186905290519116916347ef3b3b9160c480830192600092919082900301818387803b15801561295457600080fd5b505af1158015612968573d6000803e3d6000fd5b5060009250612975915050565b975092955050505050505b94509492505050565b60006116be8383604051806040016040528060158152602001747375627472616374696f6e20756e646572666c6f7760581b815250612c8b565b60006116be83836040518060400160405280601781526020017f6d756c7469706c69636174696f6e206f766572666c6f77000000000000000000815250612ce5565b612a0d612ee7565b6040518060200160405280612a3a612a34866ec097ce7bc90715b34b9f10000000006129c3565b85612d64565b90529392505050565b612a4b612ee7565b6040518060200160405280612a3a85600001518560000151612b11565b600081600160e01b841061207f5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561204457818101518382015260200161202c565b612ac5612ee7565b6040518060200160405280612a3a85600001518560000151612989565b60006ec097ce7bc90715b34b9f1000000000612b028484600001516129c3565b81612b0957fe5b049392505050565b60006116be8383604051806040016040528060118152602001706164646974696f6e206f766572666c6f7760781b815250612d97565b6000612b51612ee7565b600080612b668660000151866000015161233b565b90925090506000826003811115612b7957fe5b14612b9857506040805160208101909152600081529092509050612334565b600080612bad6706f05b59d3b2000084612312565b90925090506000826003811115612bc057fe5b14612be257506040805160208101909152600081529094509250612334915050565b600080612bf783670de0b6b3a76400006123e8565b90925090506000826003811115612c0a57fe5b14612c1157fe5b604080516020810190915290815260009a909950975050505050505050565b6000806000612c3d612ee7565b612c478787612dec565b90925090506000826003811115612c5a57fe5b14612c6b575091506000905061230a565b612c7d612c7782612e54565b86612312565b935093505050935093915050565b60008184841115612cdd5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561204457818101518382015260200161202c565b505050900390565b6000831580612cf2575082155b15612cff575060006116be565b83830283858281612d0c57fe5b04148390612d5b5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561204457818101518382015260200161202c565b50949350505050565b60006116be83836040518060400160405280600e81526020016d646976696465206279207a65726f60901b815250612e63565b60008383018285821015612d5b5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561204457818101518382015260200161202c565b6000612df6612ee7565b600080612e0786600001518661233b565b90925090506000826003811115612e1a57fe5b14612e3957506040805160208101909152600081529092509050612334565b60408051602081019091529081526000969095509350505050565b51670de0b6b3a7640000900490565b60008183612eb25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561204457818101518382015260200161202c565b50828481612ebc57fe5b04949350505050565b6040805160608101909152806000815260200160008152602001600081525090565b6040518060200160405280600081525090565b60405180610160016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001612f3f612ee7565b8152602001612f4c612ee7565b8152602001612f59612ee7565b8152602001612f66612ee7565b90529056fe4241495f4255524e5f414d4f554e545f43414c43554c4154494f4e5f4641494c45444241495f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f414d4f554e545f5345495a455f4641494c45444241495f4d494e545f414d4f554e545f43414c43554c4154494f4e5f4641494c45446f6e6c7920756e6974726f6c6c65722061646d696e2063616e206368616e676520627261696e73a265627a7a7231582030e1b18679861b7bbfd646c7ca026ff14dade35f83b8d65faa9ec7b67ea3ba3564736f6c63430005100032

Deployed ByteCode Sourcemap

130566:21776:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;130566:21776:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;115864:34;;;:::i;:::-;;;;-1:-1:-1;;;;;115864:34:0;;;;;;;;;;;;;;;;;;;;;;116425:30;;;:::i;:::-;;;;;;;;;;;;;;;;146303:239;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;146303:239:0;-1:-1:-1;;;;;146303:239:0;;:::i;:::-;;115236:27;;;:::i;:::-;;;;-1:-1:-1;;;;;115236:27:0;;;;;;;;;;;;;;151394:91;;;:::i;145836:459::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;145836:459:0;-1:-1:-1;;;;;145836:459:0;;:::i;142491:659::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;142491:659:0;;:::i;135199:592::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;135199:592:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;115332:42;;;:::i;115956:33::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;115569:39;;;:::i;151729:184::-;;;:::i;132278:2869::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;132278:2869:0;;:::i;116247:31::-;;;:::i;137743:773::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;137743:773:0;;;;;;;;;;;;;;;;;:::i;143240:1042::-;;;:::i;116321:30::-;;;:::i;150386:1000::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;150386:1000:0;;;;;;;;;;;;;;;;;:::i;144440:1153::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;144440:1153:0;-1:-1:-1;;;;;144440:1153:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;151598:123;;;:::i;116080:51::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;116080:51:0;-1:-1:-1;;;;;116080:51:0;;:::i;147234:3144::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;147234:3144:0;-1:-1:-1;;;;;147234:3144:0;;:::i;131144:48::-;;;:::i;:::-;;;;-1:-1:-1;;;;;131144:48:0;;;;;;;;;;;;;;115444:49;;;:::i;115135:20::-;;;:::i;115864:34::-;;;-1:-1:-1;;;;;115864:34:0;;;-1:-1:-1;;;115864:34:0;;;;;:::o;116425:30::-;;;;:::o;146303:239::-;146388:10;-1:-1:-1;;;;;146388:16:0;;:18;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146388:18:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146388:18:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;146388:18:0;-1:-1:-1;;;;;146374:32:0;:10;:32;146366:84;;;;-1:-1:-1;;;146366:84:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;146469:10;-1:-1:-1;;;;;146469:32:0;;:34;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;146469:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;146469:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;146469:34:0;:39;146461:73;;;;;-1:-1:-1;;;146461:73:0;;;;;;;;;;;;-1:-1:-1;;;146461:73:0;;;;;;;;;;;;;;;146303:239;:::o;115236:27::-;;;-1:-1:-1;;;;;115236:27:0;;:::o;151394:91::-;151465:12;151394:91;:::o;145836:459::-;145914:4;145983:5;;-1:-1:-1;;;;;145983:5:0;145969:10;:19;145965:124;;146012:65;146017:18;146037:39;146012:4;:65::i;:::-;146005:72;;;;145965:124;146139:11;;;-1:-1:-1;;;;;146161:26:0;;;-1:-1:-1;;;;;;146161:26:0;;;;;;;146203:44;;;146139:11;;;;146203:44;;;;;;;;;;;;;;;;;;;;;;;146272:14;146260:27;;;145836:459;;;;:::o;142491:659::-;142561:4;142630:5;;-1:-1:-1;;;;;142630:5:0;142616:10;:19;142612:124;;142659:65;142664:18;142684:39;142659:4;:65::i;142612:124::-;142752:21;;;;142748:355;;142799:21;:28;;-1:-1:-1;;142799:28:0;142823:4;142799:28;;;:21;142864:16;;:49;;142902:11;142864:49;;;142883:16;:14;:16::i;:::-;142842:71;;142944:147;;;;;;;;131188:4;-1:-1:-1;;;;;142944:147:0;;;;;143027:48;143034:14;143027:48;;;;;;;;;;;;;-1:-1:-1;;;143027:48:0;;;:6;:48::i;:::-;142944:147;;;;;;;142928:163;;:13;:163;;;;;;;;;;-1:-1:-1;;;142928:163:0;-1:-1:-1;;;;;142928:163:0;;;-1:-1:-1;;;;;;142928:163:0;;;;;;;;;;;;;;-1:-1:-1;142748:355:0;143127:14;143115:27;142491:659;-1:-1:-1;;142491:659:0:o;135199:592::-;152200:11;;135269:4;;;;152200:11;;152192:34;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;;;;152237:11;:19;;-1:-1:-1;;152237:19:0;;;135303:11;;-1:-1:-1;;;;;135303:11:0;135295:34;135292:492;;135371:1;135354:14;:18;135346:59;;;;;-1:-1:-1;;;135346:59:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;135464:11;;;135431:63;;;-1:-1:-1;;;135431:63:0;;;;-1:-1:-1;;;;;135464:11:0;;;;135431:61;;:63;;;;;;;;;;;;135464:11;135431:63;;;5:2:-1;;;;30:1;27;20:12;5:2;135431:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;135431:63:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;135431:63:0;135430:64;135422:95;;;;;-1:-1:-1;;;135422:95:0;;;;;;;;;;;;-1:-1:-1;;;135422:95:0;;;;;;;;;;;;;;;135550:10;135577:25;:23;:25::i;:::-;-1:-1:-1;135650:11:0;;;135617:78;;;-1:-1:-1;;;135617:78:0;;-1:-1:-1;;;;;135617:78:0;;;;;;;;;;;;135650:11;;;;;135617:71;;:78;;;;;135650:11;;135617:78;;;;;;;;135650:11;;135617:78;;;5:2:-1;;;;30:1;27;20:12;5:2;135617:78:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;135617:78:0;;;;135719:53;135733:10;135745;135757:14;135719:13;:53::i;:::-;135712:60;;;;;135292:492;152279:11;:18;;-1:-1:-1;;152279:18:0;152293:4;152279:18;;;135199:592;;;;-1:-1:-1;135199:592:0:o;115332:42::-;;;-1:-1:-1;;;;;115332:42:0;;:::o;115956:33::-;;;;;;:::o;115569:39::-;;;-1:-1:-1;;;;;115569:39:0;;:::o;151729:184::-;151975:5;;-1:-1:-1;;;;;151975:5:0;151961:10;:19;151953:46;;;;;-1:-1:-1;;;151953:46:0;;;;;;;;;;;;-1:-1:-1;;;151953:46:0;;;;;;;;;;;;;;;151887:11;:18;;-1:-1:-1;;151887:18:0;151901:4;151887:18;;;151729:184::o;132278:2869::-;152200:11;;132346:4;;152200:11;;152192:34;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;;;;152237:11;:19;;-1:-1:-1;;152237:19:0;;;132374:11;;-1:-1:-1;;;;;132374:11:0;132366:34;132363:2777;;132441:1;132425:13;:17;132417:57;;;;;-1:-1:-1;;;132417:57:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;132533:11;;;132500:63;;;-1:-1:-1;;;132500:63:0;;;;-1:-1:-1;;;;;132533:11:0;;;;132500:61;;:63;;;;;;;;;;;;132533:11;132500:63;;;5:2:-1;;;;30:1;27;20:12;5:2;132500:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;132500:63:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;132500:63:0;132499:64;132491:95;;;;;-1:-1:-1;;;132491:95:0;;;;;;;;;;;;-1:-1:-1;;;132491:95:0;;;;;;;;;;;;;;;132603:25;;:::i;:::-;132662:10;132730:25;:23;:25::i;:::-;-1:-1:-1;132803:11:0;;;132770:79;;;-1:-1:-1;;;132770:79:0;;-1:-1:-1;;;;;132770:79:0;;;;;;;;;;;;132803:11;;;;;132770:71;;:79;;;;;132803:11;;132770:79;;;;;;;;132803:11;;132770:79;;;5:2:-1;;;;30:1;27;20:12;5:2;132770:79:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;132770:79:0;;;;132866:9;132890:14;132919:22;132956:23;133025:22;133040:6;133025:14;:22::i;:::-;132996:51;;-1:-1:-1;132996:51:0;-1:-1:-1;133066:28:0;;133062:97;;133127:15;133122:21;133115:28;;;;;;;;;;133062:97;133263:18;133247:13;:34;133243:135;;;133309:53;133314:15;133331:30;133309:4;:53::i;133243:135::-;133463:11;;;133430:65;;;-1:-1:-1;;;133430:65:0;;-1:-1:-1;;;;;133430:65:0;;;;;;;;;;;;133422:89;;133463:11;;;;133430:57;;:65;;;;;;;;;;;;;;;133463:11;133430:65;;;5:2:-1;;;;30:1;27;20:12;5:2;133430:65:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;133430:65:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;133430:65:0;133497:13;133422:7;:89::i;:::-;133394:117;;-1:-1:-1;133394:117:0;-1:-1:-1;133542:18:0;133534:4;:26;;;;;;;;;133526:73;;;;-1:-1:-1;;;133526:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;133627:11;;;:53;;;-1:-1:-1;;;133627:53:0;;-1:-1:-1;;;;;133627:53:0;;;;;;;;;;;;;;;;;;133614:10;;133627:11;;;;:26;;:53;;;;;;;;;;;;;;;133614:10;133627:11;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;133627:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;133627:53:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;133627:53:0;;-1:-1:-1;133699:10:0;;133695:64;;133738:5;-1:-1:-1;133731:12:0;;-1:-1:-1;;;;;;133731:12:0;133695:64;133838:15;;;:31;;;133888:15;;133775:14;;;;133888:20;133884:1082;;133957:41;133965:4;:15;;;133982;;133957:7;:41::i;:::-;133930:4;:12;;133929:69;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;134037:18:0;;-1:-1:-1;134021:4:0;:12;;;:34;;;;;;;;;134017:179;;134087:89;134098:16;134116:39;134162:4;:12;;;134157:18;;;;;;;;134087:10;:89::i;:::-;134080:96;;;;;;;;;;;;;134017:179;134244:24;134252:9;134263:4;134244:7;:24::i;:::-;134217:4;:12;;134216:52;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;134307:18:0;;-1:-1:-1;134291:4:0;:12;;;:34;;;;;;;;;134287:179;;134357:89;134368:16;134386:39;134432:4;:12;;;134427:18;;;;;;;134287:179;134519:35;134527:4;:15;;;134544:9;134519:7;:35::i;:::-;134487:4;:12;;134486:68;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;134593:18:0;;-1:-1:-1;134577:4:0;:12;;;:34;;;;;;;;;134573:179;;134643:89;134654:16;134672:39;134718:4;:12;;;134713:18;;;;;;;134573:179;134776:15;:13;:15::i;:::-;134798;;134772:53;;;-1:-1:-1;;;134772:53:0;;-1:-1:-1;;;;;134798:15:0;;;134772:53;;;;;;;;;;;;:25;;;;;;;:53;;;;;134798:15;;134772:53;;;;;;;;134798:15;134772:25;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;134772:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;134851:26:0;;;-1:-1:-1;;;;;134851:26:0;;;;;;;;;;;;;;-1:-1:-1;134851:26:0;;;;;;;;-1:-1:-1;134851:26:0;133884:1082;;;-1:-1:-1;134935:15:0;;;;133884:1082;134986:15;:13;:15::i;:::-;-1:-1:-1;;;;;134982:25:0;;135008:6;135016:14;134982:49;;;;;;;;;;;;;-1:-1:-1;;;;;134982:49:0;-1:-1:-1;;;;;134982:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;134982:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;135053:31:0;;;-1:-1:-1;;;;;135053:31:0;;;;;;;;;;;;;;-1:-1:-1;135053:31:0;;;;;;;;-1:-1:-1;135053:31:0;135113:14;135108:20;;132363:2777;152279:11;:18;;-1:-1:-1;;152279:18:0;152293:4;152279:18;;;132278:2869;;-1:-1:-1;132278:2869:0:o;116247:31::-;;;-1:-1:-1;;;;;116247:31:0;;:::o;137743:773::-;152200:11;;137866:4;;;;152200:11;;152192:34;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;-1:-1:-1;;;152192:34:0;;;;;;;;;;;;;;;152237:11;:19;;-1:-1:-1;;152237:19:0;;;137931:11;;;137898:63;;;-1:-1:-1;;;137898:63:0;;;;-1:-1:-1;;;;;137931:11:0;;;;137898:61;;:63;;;;;;;;;;;;137931:11;137898:63;;;5:2:-1;;;;30:1;27;20:12;5:2;137898:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;137898:63:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;137898:63:0;137897:64;137889:95;;;;;-1:-1:-1;;;137889:95:0;;;;;;;;;;;;-1:-1:-1;;;137889:95:0;;;;;;;;;;;;;;;137997:10;138010:16;-1:-1:-1;;;;;138010:31:0;;:33;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;138010:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;138010:33:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;138010:33:0;;-1:-1:-1;138058:29:0;;138054:277;;138236:79;138247:5;138241:12;;;;;;;;138255:59;138236:4;:79::i;:::-;138228:91;-1:-1:-1;138317:1:0;;-1:-1:-1;138228:91:0;;-1:-1:-1;138228:91:0;138054:277;138438:70;138456:10;138468:8;138478:11;138491:16;138438:17;:70::i;:::-;138431:77;;;;;152267:1;152279:11;:18;;-1:-1:-1;;152279:18:0;152293:4;152279:18;;;137743:773;;;;-1:-1:-1;137743:773:0;-1:-1:-1;;137743:773:0:o;143240:1042::-;143363:11;;;143330:61;;;-1:-1:-1;;;143330:61:0;;;;143291:4;;;;-1:-1:-1;;;;;143363:11:0;;143330:59;;:61;;;;;;;;;;;143363:11;143330:61;;;5:2:-1;;;;30:1;27;20:12;5:2;143330:61:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143330:61:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;143330:61:0;;-1:-1:-1;143402:16:0;143421;:14;:16::i;:::-;143490:13;:19;143402:35;;-1:-1:-1;143448:16:0;;143467:44;;143402:35;;-1:-1:-1;;;143490:19:0;;;;143467:4;:44::i;:::-;143448:63;;143540:1;143526:11;:15;:37;;;;;143562:1;143545:14;:18;143526:37;143522:713;;;143580:14;143601:15;:13;:15::i;:::-;-1:-1:-1;;;;;143597:32:0;;:34;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;143597:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;143597:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;143597:34:0;;-1:-1:-1;143646:17:0;143666:33;143671:11;143684:14;143666:4;:33::i;:::-;143646:53;;143714:19;;:::i;:::-;143748:1;143736:9;:13;:73;;143788:21;;;;;;;;143806:1;143788:21;;;143736:73;;;143752:33;143761:12;143775:9;143752:8;:33::i;:::-;143714:95;;143824:19;;:::i;:::-;143851:39;;;;;;;;;143869:13;:19;-1:-1:-1;;;;;143869:19:0;143851:39;;143846:52;;143892:5;143846:4;:52::i;:::-;143824:74;;143929:173;;;;;;;;143969:46;143977:5;:14;;;143969:46;;;;;;;;;;;;;-1:-1:-1;;;143969:46:0;;;:7;:46::i;:::-;-1:-1:-1;;;;;143929:173:0;;;;;144041:45;144048:11;144041:45;;;;;;;;;;;;;-1:-1:-1;;;144041:45:0;;;:6;:45::i;:::-;143929:173;;;;;;;143913:189;;:13;:189;;;;;;;;;;-1:-1:-1;;;143913:189:0;-1:-1:-1;;;;;143913:189:0;;;-1:-1:-1;;;;;;143913:189:0;;;;;;;;;;;;;;-1:-1:-1;143522:713:0;;-1:-1:-1;;;143522:713:0;;144124:15;;144120:115;;144178:45;144185:11;144178:45;;;;;;;;;;;;;-1:-1:-1;;;144178:45:0;;;:6;:45::i;:::-;144156:13;:67;;;;;;;-1:-1:-1;;;144156:67:0;-1:-1:-1;;;;;144156:67:0;;;;;;;;;144120:115;144259:14;144247:27;;;;;143240:1042;:::o;116321:30::-;;;-1:-1:-1;;;;;116321:30:0;;:::o;150386:1000::-;150512:4;150583:5;;-1:-1:-1;;;;;150583:5:0;150569:10;:19;;:53;;-1:-1:-1;150606:16:0;;-1:-1:-1;;;;;150606:16:0;150592:10;:30;150569:53;150563:158;;150647:62;150652:18;150672:36;150647:4;:62::i;:::-;150640:69;;;;150563:158;150762:4;150741:18;:25;150733:67;;;;;-1:-1:-1;;;150733:67:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;150843:16;;;150899:15;;;150951;;;-1:-1:-1;;;;;150979:38:0;;;-1:-1:-1;;;;;;150979:38:0;;;;;;;;151028:36;;;;;;;;;;;;;151075;;;;151129:61;;;150843:16;;;151129:61;;;;;;;;;;;;150899:15;;;;;150951;;151129:61;;;;;;;;;151206:58;;;-1:-1:-1;;;;;151206:58:0;;;;;;;;;;;;;;;;;;;;;;;;151280;;;;;;;;;;;;;;;;;;;;;;;;;151363:14;151351:27;;;;;150386:1000;;;;;;:::o;144440:1153::-;144613:11;;144512:4;;;;;;;;-1:-1:-1;;;;;144613:11:0;144591:10;:34;144587:150;;144650:65;144655:18;144675:39;144650:4;:65::i;:::-;144642:83;-1:-1:-1;144717:1:0;;-1:-1:-1;144717:1:0;;-1:-1:-1;144717:1:0;;-1:-1:-1;144642:83:0;;144587:150;144749:26;;:::i;:::-;-1:-1:-1;144778:39:0;;;;;;;;;144796:13;:19;-1:-1:-1;;;;;144796:19:0;144778:39;;144828:28;;:::i;:::-;-1:-1:-1;144859:50:0;;;;;;;;;-1:-1:-1;;;;;144877:30:0;;-1:-1:-1;144877:30:0;;;:19;:30;;;;;;;;144859:50;;144953:21;;144920:30;;;;;;;;:54;;;144991:23;;:28;:57;;;;-1:-1:-1;145023:21:0;;:25;;144991:57;144987:133;;;131188:4;145065:43;;144987:133;145132:24;;:::i;:::-;145159:34;145164:12;145178:14;145159:4;:34::i;:::-;145132:61;;145204:20;145260:11;;;;;;;;;-1:-1:-1;;;;;145260:11:0;-1:-1:-1;;;;;145227:57:0;;145285:9;145227:68;;;;;;;;;;;;;-1:-1:-1;;;;;145227:68:0;-1:-1:-1;;;;;145227:68:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;145227:68:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;145227:68:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;145227:68:0;;-1:-1:-1;145306:19:0;145328:33;145227:68;145350:10;145328:4;:33::i;:::-;145306:55;;145372:21;145396:92;145434:11;;;;;;;;;-1:-1:-1;;;;;145434:11:0;-1:-1:-1;;;;;145401:59:0;;145461:9;145401:70;;;;;;;;;;;;;-1:-1:-1;;;;;145401:70:0;-1:-1:-1;;;;;145401:70:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;145401:70:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;145401:70:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;145401:70:0;145473:14;145396:4;:92::i;:::-;145372:116;-1:-1:-1;145512:14:0;145563:21;;145499:86;;-1:-1:-1;145529:16:0;-1:-1:-1;145547:14:0;-1:-1:-1;145563:21:0;;-1:-1:-1;;;;144440:1153:0;;;;;;:::o;151598:123::-;151671:42;151598:123;:::o;116080:51::-;;;;;;;;;;;;;:::o;147234:3144::-;147295:4;147301;147318:18;147372:11;;;;;;;;;-1:-1:-1;;;;;147372:11:0;-1:-1:-1;;;;;147339:53:0;;:55;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;147339:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;147339:55:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;147339:55:0;147471:11;;;147438:66;;;-1:-1:-1;;;147438:66:0;;-1:-1:-1;;;;;147438:66:0;;;;;;;;;;;;147339:55;;-1:-1:-1;147405:30:0;;147471:11;;;;;147438:58;;:66;;;;;147471:11;;147438:66;;;;;;;;147471:11;147438:66;;;5:2:-1;;;;30:1;27;20:12;5:2;147438:66:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;147438:66:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;147438:66:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;147438:66:0;;;;;;;;;;;;;19:11:-1;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;261:11;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;147438:66:0;;421:4:-1;412:14;;;;147438:66:0;;;;;412:14:-1;147438:66:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;147438:66:0;;;;;;;;;;;147405:99;;147517:34;;:::i;:::-;147601:9;;;;147873:1612;147889:14;:21;147885:1;:25;147873:1612;;;148008:14;148023:1;148008:17;;;;;;;;;;;;;;-1:-1:-1;;;;;148008:36:0;;148045:6;148008:44;;;;;;;;;;;;;-1:-1:-1;;;;;148008:44:0;-1:-1:-1;;;;;148008:44:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148008:44:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148008:44:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;148008:44:0;;;;;;;;;;;;;;;147979:25;;;147932:120;148008:44;147959:18;;147932:120;147939:18;;;147932:120;;;;148008:44;-1:-1:-1;148071:9:0;;148067:169;;148195:20;148190:26;148182:38;-1:-1:-1;148218:1:0;;-1:-1:-1;148182:38:0;;-1:-1:-1;;;;;;;148182:38:0;148067:169;148270:42;;;;;;;;148285:4;:25;;;148270:42;;;148250:4;:17;;:62;;;;148410:6;-1:-1:-1;;;;;148410:25:0;;148436:14;148451:1;148436:17;;;;;;;;;;;;;;148410:44;;;;;;;;;;;;;-1:-1:-1;;;;;148410:44:0;-1:-1:-1;;;;;148410:44:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;148410:44:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;148410:44:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;148410:44:0;148383:24;;;:71;;;148469:105;;148536:17;148531:23;;148469:105;148607:41;;;;;;;;;148622:24;;;;148607:41;;148588:16;;;:60;;;148701:17;;;;148694:43;;:6;:43::i;:::-;148672:18;;;148665:72;;-1:-1:-1;148764:18:0;148756:4;:26;;;;;;;;;148752:101;;148816:16;148811:22;;148752:101;148953:80;148978:4;:18;;;148998:4;:18;;;149018:4;:14;;;148953:24;:80::i;:::-;148935:14;;;148928:105;;-1:-1:-1;149060:18:0;149052:4;:26;;;;;;;;;149048:101;;149112:16;149107:22;;149048:101;149269:89;149294:4;:16;;;149312:4;:18;;;149332:4;:25;;;149269:24;:89::i;:::-;149240:25;;;149233:125;;-1:-1:-1;149385:18:0;149377:4;:26;;;;;;;;;149373:101;;149437:16;149432:22;;149373:101;147912:3;;147873:1612;;;149541:25;;;;;149601:11;;;149568:65;;-1:-1:-1;;;149568:65:0;;-1:-1:-1;;;;;149568:65:0;;;;;;;;;;;;149533:101;;149601:11;;;;;149568:57;;:65;;;;;;;;;;;;;;149601:11;149568:65;;;5:2:-1;;;;30:1;27;20:12;5:2;149568:65:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149568:65:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;149568:65:0;149533:7;:101::i;:::-;149504:25;;;149497:137;;-1:-1:-1;149657:18:0;149649:4;:26;;;;;;;;;149645:93;;149705:16;149700:22;;149645:93;149779:85;149787:4;:14;;;149836:11;;;;;;;;;-1:-1:-1;;;;;149836:11:0;-1:-1:-1;;;;;149803:58:0;;:60;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;149803:60:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;149803:60:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;149803:60:0;149779:7;:85::i;:::-;149750:114;;-1:-1:-1;149750:114:0;-1:-1:-1;149891:18:0;149883:4;:26;;;;;;;;;149875:73;;;;-1:-1:-1;;;149875:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;149990:34;149998:18;150018:5;149990:7;:34::i;:::-;149961:63;;-1:-1:-1;149961:63:0;-1:-1:-1;150051:18:0;150043:4;:26;;;;;;;;;150035:73;;;;-1:-1:-1;;;150035:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;150152:54;150160:18;150180:4;:25;;;150152:7;:54::i;:::-;150123:83;;-1:-1:-1;150123:83:0;-1:-1:-1;150229:18:0;150221:4;:26;;;;;;;;;150217:92;;150277:15;150272:21;;150217:92;-1:-1:-1;150334:14:0;;-1:-1:-1;150351:18:0;-1:-1:-1;;;;;;147234:3144:0;;;;:::o;131144:48::-;131188:4;131144:48;:::o;115444:49::-;;;-1:-1:-1;;;;;115444:49:0;;:::o;115135:20::-;;;-1:-1:-1;;;;;115135:20:0;;:::o;25196:153::-;25257:4;25279:33;25292:3;25287:9;;;;;;;;25303:4;25298:10;;;;;;;;25279:33;;;;;;;;;;;;;25310:1;25279:33;;;;;;;;;;;;;25337:3;25332:9;;;;;;;30723:161;30798:6;30836:12;30829:5;30825:9;;30817:32;;;;-1:-1:-1;;;30817:32:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;30817:32:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;30874:1:0;;30723:161;-1:-1:-1;;30723:161:0:o;136245:977::-;136337:4;136343;136360:21;136394:23;136453:11;;;;;;;;;-1:-1:-1;;;;;136453:11:0;-1:-1:-1;;;;;136420:57:0;;136478:8;136420:67;;;;;;;;;;;;;-1:-1:-1;;;;;136420:67:0;-1:-1:-1;;;;;136420:67:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;136420:67:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;136420:67:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;136420:67:0;;-1:-1:-1;136503:32:0;;;136500:164;;;136571:11;136552:30;;136500:164;;;136634:18;136615:37;;136500:164;136676:14;136701:18;136736:15;:13;:15::i;:::-;-1:-1:-1;;;;;136732:25:0;;136758:5;136765:16;136732:50;;;;;;;;;;;;;-1:-1:-1;;;;;136732:50:0;-1:-1:-1;;;;;136732:50:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;136732:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;136732:50:0;;;;136819:45;136827:18;136847:16;136819:7;:45::i;:::-;136795:69;;-1:-1:-1;136795:69:0;-1:-1:-1;136891:18:0;136883:4;:26;;;;;;;;;136875:73;;;;-1:-1:-1;;;136875:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;136974:11;;;:51;;;-1:-1:-1;;;136974:51:0;;-1:-1:-1;;;;;136974:51:0;;;;;;;;;;;;;;;;;;136961:10;;136974:11;;;;:26;;:51;;;;;;;;;;;;;;;136961:10;136974:11;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;136974:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;136974:51:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;136974:51:0;;-1:-1:-1;137040:10:0;;137036:60;;137075:5;-1:-1:-1;137082:1:0;;-1:-1:-1;137067:17:0;;-1:-1:-1;;;;137067:17:0;137036:60;137111:43;;;-1:-1:-1;;;;;137111:43:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;137180:14;137167:47;-1:-1:-1;137197:16:0;;-1:-1:-1;;;;;136245:977:0;;;;;;;:::o;27376:258::-;27432:9;;27469:5;;;27491:6;;;27487:140;;27522:18;;-1:-1:-1;27542:1:0;-1:-1:-1;27514:30:0;;27487:140;-1:-1:-1;27585:26:0;;-1:-1:-1;27613:1:0;;-1:-1:-1;27376:258:0;;;;;;:::o;26267:343::-;26323:9;;26355:6;26351:69;;-1:-1:-1;26386:18:0;;-1:-1:-1;26386:18:0;26378:30;;26351:69;26441:5;;;26445:1;26441;:5;:1;26463:5;;;;;:10;26459:144;;-1:-1:-1;26498:26:0;;-1:-1:-1;26526:1:0;;-1:-1:-1;26490:38:0;;26459:144;26569:18;;-1:-1:-1;26589:1:0;-1:-1:-1;26561:30:0;;25472:187;25557:4;25579:43;25592:3;25587:9;;;;;;;;25603:4;25598:10;;;;;;;;25579:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;25647:3;25642:9;;;;;;;;25635:16;25472:187;-1:-1:-1;;;;25472:187:0:o;26705:215::-;26761:9;;26793:6;26789:77;;-1:-1:-1;26824:26:0;;-1:-1:-1;26852:1:0;26816:38;;26789:77;26886:18;26910:1;26906;:5;;;;;;26878:34;;;;26705:215;;;;;:::o;27055:236::-;27111:9;27122:4;27148:1;27143;:6;27139:145;;-1:-1:-1;27174:18:0;;-1:-1:-1;27194:5:0;;;27166:34;;27139:145;-1:-1:-1;27241:27:0;;-1:-1:-1;27270:1:0;27233:39;;139120:3300;139289:11;;139255:4;;;;-1:-1:-1;;;;;139289:11:0;139281:34;139278:3135;;139396:11;;;:111;;;-1:-1:-1;;;139396:111:0;;139439:4;139396:111;;;;;;;-1:-1:-1;;;;;139396:111:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;139381:12;;139396:11;;;;;:34;;:111;;;;;;;;;;;;;;;139381:12;139396:11;:111;;;5:2:-1;;;;30:1;27;20:12;5:2;139396:111:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;139396:111:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;139396:111:0;;-1:-1:-1;139526:12:0;;139522:150;;139567:85;139578:15;139595:47;139644:7;139567:10;:85::i;:::-;139559:97;-1:-1:-1;139654:1:0;;-1:-1:-1;139559:97:0;;-1:-1:-1;139559:97:0;139522:150;139908:16;:14;:16::i;:::-;139867;-1:-1:-1;;;;;139867:35:0;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;139867:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;139867:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;139867:37:0;:57;139863:185;;139953:75;139958:15;139975:52;139953:4;:75::i;139863:185::-;140129:10;-1:-1:-1;;;;;140117:22:0;:8;-1:-1:-1;;;;;140117:22:0;;140113:146;;;140168:71;140173:15;140190:48;140168:4;:71::i;140113:146::-;140322:16;140318:138;;140367:69;140372:15;140389:46;140367:4;:69::i;140318:138::-;-1:-1:-1;;140520:11:0;:23;140516:149;;;140572:73;140577:15;140594:50;140572:4;:73::i;140516:149::-;140726:21;140749:22;140775:48;140789:10;140801:8;140811:11;140775:13;:48::i;:::-;140725:98;;-1:-1:-1;140725:98:0;-1:-1:-1;140842:40:0;;140838:175;;140911:82;140922:16;140916:23;;;;;;;;140941:51;140911:4;:82::i;:::-;140903:94;-1:-1:-1;140995:1:0;;-1:-1:-1;140903:94:0;;-1:-1:-1;;;140903:94:0;140838:175;141290:11;;;:90;;;-1:-1:-1;;;141290:90:0;;-1:-1:-1;;;;;141290:90:0;;;;;;;;;;;;;;;;;;141247:21;;;;141290:11;;:44;;:90;;;;;;;;;;;:11;:90;;;5:2:-1;;;;30:1;27;20:12;5:2;141290:90:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141290:90:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141290:90:0;;;;;;;;;-1:-1:-1;141290:90:0;-1:-1:-1;141403:40:0;;141395:108;;;;-1:-1:-1;;;141395:108:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;141645:11;141605:16;-1:-1:-1;;;;;141605:26:0;;141632:8;141605:36;;;;;;;;;;;;;-1:-1:-1;;;;;141605:36:0;-1:-1:-1;;;;;141605:36:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;141605:36:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141605:36:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141605:36:0;:51;;141597:92;;;;;-1:-1:-1;;;141597:92:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;141749:57;;;-1:-1:-1;;;141749:57:0;;-1:-1:-1;;;;;141749:57:0;;;;;;;;;;;;;;;;;;;;;;141706:15;;141749:22;;;;;:57;;;;;;;;;;;;;;141706:15;141749:22;:57;;;5:2:-1;;;;30:1;27;20:12;5:2;141749:57:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;141749:57:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;141749:57:0;;-1:-1:-1;141921:34:0;;141913:67;;;;;-1:-1:-1;;;141913:67:0;;;;;;;;;;;;-1:-1:-1;;;141913:67:0;;;;;;;;;;;;;;;142053:93;;;-1:-1:-1;;;;;142053:93:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;142207:11;;;:129;;;-1:-1:-1;;;142207:129:0;;142249:4;142207:129;;;;;;;-1:-1:-1;;;;;142207:129:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:11;;;:33;;:129;;;;;:11;;:129;;;;;;;:11;;:129;;;5:2:-1;;;;30:1;27;20:12;5:2;142207:129:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;142366:14:0;;-1:-1:-1;142361:20:0;;-1:-1:-1;;142361:20:0;;142353:48;-1:-1:-1;142383:17:0;;-1:-1:-1;;;;;;139278:3135:0;139120:3300;;;;;;;:::o;31851:120::-;31904:4;31928:35;31933:1;31936;31928:35;;;;;;;;;;;;;-1:-1:-1;;;31928:35:0;;;:4;:35::i;33049:122::-;33102:4;33126:37;33131:1;33134;33126:37;;;;;;;;;;;;;;;;;:4;:37::i;34647:147::-;34704:13;;:::i;:::-;34737:49;;;;;;;;34755:29;34760:20;34765:1;28485:4;34760;:20::i;:::-;34782:1;34755:4;:29::i;:::-;34737:49;;34730:56;34647:147;-1:-1:-1;;;34647:147:0:o;31048:160::-;31119:13;;:::i;:::-;31152:48;;;;;;;;31170:28;31175:1;:10;;;31187:1;:10;;;31170:4;:28::i;30550:165::-;30626:7;30666:12;-1:-1:-1;;;30654:10:0;;30646:33;;;;-1:-1:-1;;;30646:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;31683:160:0;31754:13;;:::i;:::-;31787:48;;;;;;;;31805:28;31810:1;:10;;;31822:1;:10;;;31805:4;:28::i;32914:127::-;32976:4;28485;33000:19;33005:1;33008;:10;;;33000:4;:19::i;:::-;:33;;;;;;;32914:127;-1:-1:-1;;;32914:127:0:o;31216:116::-;31269:4;31293:31;31298:1;31301;31293:31;;;;;;;;;;;;;-1:-1:-1;;;31293:31:0;;;:4;:31::i;39653:1136::-;39720:9;39731:10;;:::i;:::-;39757:14;39773:24;39801:31;39809:1;:10;;;39821:1;:10;;;39801:7;:31::i;:::-;39756:76;;-1:-1:-1;39756:76:0;-1:-1:-1;39855:18:0;39847:4;:26;;;;;;;;;39843:92;;-1:-1:-1;39904:18:0;;;;;;;;;-1:-1:-1;39904:18:0;;39898:4;;-1:-1:-1;39904:18:0;-1:-1:-1;39890:33:0;;39843:92;40252:14;;40309:42;28525:10;40331:19;40309:7;:42::i;:::-;40251:100;;-1:-1:-1;40251:100:0;-1:-1:-1;40374:18:0;40366:4;:26;;;;;;;;;40362:92;;-1:-1:-1;40423:18:0;;;;;;;;;-1:-1:-1;40423:18:0;;40417:4;;-1:-1:-1;40423:18:0;-1:-1:-1;40409:33:0;;-1:-1:-1;;40409:33:0;40362:92;40467:14;40483:12;40499:51;40507:32;28446:4;40499:7;:51::i;:::-;40466:84;;-1:-1:-1;40466:84:0;-1:-1:-1;40696:18:0;40688:4;:26;;;;;;;;;40681:34;;;;40756:24;;;;;;;;;;;;-1:-1:-1;;40756:24:0;;-1:-1:-1;39653:1136:0;-1:-1:-1;;;;;;;;39653:1136:0:o;37629:328::-;37726:9;37737:4;37755:13;37770:18;;:::i;:::-;37792:20;37802:1;37805:6;37792:9;:20::i;:::-;37754:58;;-1:-1:-1;37754:58:0;-1:-1:-1;37834:18:0;37827:3;:25;;;;;;;;;37823:73;;-1:-1:-1;37877:3:0;-1:-1:-1;37882:1:0;;-1:-1:-1;37869:15:0;;37823:73;37915:34;37923:17;37932:7;37923:8;:17::i;:::-;37942:6;37915:7;:34::i;:::-;37908:41;;;;;;37629:328;;;;;;:::o;31979:158::-;32060:4;32093:12;32085:6;;;;32077:29;;;;-1:-1:-1;;;32077:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;32077:29:0;-1:-1:-1;;;32124:5:0;;;31979:158::o;33179:250::-;33260:4;33281:6;;;:16;;-1:-1:-1;33291:6:0;;33281:16;33277:57;;;-1:-1:-1;33321:1:0;33314:8;;33277:57;33353:5;;;33357:1;33353;:5;:1;33377:5;;;;;:10;33389:12;33369:33;;;;;-1:-1:-1;;;33369:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;33369:33:0;-1:-1:-1;33420:1:0;33179:250;-1:-1:-1;;;;33179:250:0:o;34361:113::-;34414:4;34438:28;34443:1;34446;34438:28;;;;;;;;;;;;;-1:-1:-1;;;34438:28:0;;;:4;:28::i;31340:179::-;31421:4;31447:5;;;31479:12;31471:6;;;;31463:29;;;;-1:-1:-1;;;31463:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;36705:353:0;36774:9;36785:10;;:::i;:::-;36809:14;36825:19;36848:27;36856:1;:10;;;36868:6;36848:7;:27::i;:::-;36808:67;;-1:-1:-1;36808:67:0;-1:-1:-1;36898:18:0;36890:4;:26;;;;;;;;;36886:92;;-1:-1:-1;36947:18:0;;;;;;;;;-1:-1:-1;36947:18:0;;36941:4;;-1:-1:-1;36947:18:0;-1:-1:-1;36933:33:0;;36886:92;37018:31;;;;;;;;;;;;-1:-1:-1;;37018:31:0;;-1:-1:-1;36705:353:0;-1:-1:-1;;;;36705:353:0:o;28842:213::-;29024:12;28446:4;29024:23;;;28842:213::o;34482:157::-;34563:4;34595:12;34588:5;34580:28;;;;-1:-1:-1;;;34580:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;34580:28:0;;34630:1;34626;:5;;;;;;;34482:157;-1:-1:-1;;;;34482:157:0:o;130566:21776::-;;;;;;;;;;;-1:-1:-1;130566:21776:0;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;:::o

Swarm Source

bzzr://30e1b18679861b7bbfd646c7ca026ff14dade35f83b8d65faa9ec7b67ea3ba35
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading