ETH Price: $2,435.56 (-4.64%)

Transaction Decoder

Block:
12777124 at Jul-07-2021 12:26:47 AM +UTC
Transaction Fee:
0.005379103 ETH $13.10
Gas Used:
114,449 Gas / 47 Gwei

Emitted Events:

159 Share.Transfer( from=[Sender] 0x2f686f1cf743bd2d274a61dd12ca6eb8af65c745, to=[Receiver] 0xfc554db7c8ef61db6ec1f43f96a4da539e55ea88, value=15592000000000000000 )
160 Share.Approval( owner=[Sender] 0x2f686f1cf743bd2d274a61dd12ca6eb8af65c745, spender=[Receiver] 0xfc554db7c8ef61db6ec1f43f96a4da539e55ea88, value=115792089237316195423570985008687907853269984665640564039441992007913129639935 )
161 ShareV2.Transfer( from=[Receiver] 0xfc554db7c8ef61db6ec1f43f96a4da539e55ea88, to=[Sender] 0x2f686f1cf743bd2d274a61dd12ca6eb8af65c745, value=15592000000000000000 )
162 0xfc554db7c8ef61db6ec1f43f96a4da539e55ea88.0xd9cb1e2714d65a111c0f20f060176ad657496bd47a3de04ec7c3d4ca232112ac( 0xd9cb1e2714d65a111c0f20f060176ad657496bd47a3de04ec7c3d4ca232112ac, 00000000000000000000000000000000000000000000000000000000000000ea, 0000000000000000000000002f686f1cf743bd2d274a61dd12ca6eb8af65c745, 000000000000000000000000000000000000000000000000d8645774774eb000, 000000000000000000000000000000000000000000000000d861e963d9e40000 )

Account State Difference:

  Address   Before After State Difference Code
0x106538CC...A23875287
0x2F686f1c...8AF65c745
0.914920041855029844 Eth
Nonce: 322
0.909540938855029844 Eth
Nonce: 323
0.005379103
(F2Pool Old)
3,506.066996008621864004 Eth3,506.072375111621864004 Eth0.005379103
0xa7ED29B2...f81B63696
0xfc554DB7...39e55EA88

Execution Trace

0xfc554db7c8ef61db6ec1f43f96a4da539e55ea88.3e4fcb21( )
  • Share.transferFrom( sender=0x2F686f1cF743bD2D274a61Dd12cA6eB8AF65c745, recipient=0xfc554DB7C8Ef61db6ec1f43F96a4dA539e55EA88, amount=15592000000000000000 ) => ( True )
  • ShareV2.transfer( recipient=0x2F686f1cF743bD2D274a61Dd12cA6eB8AF65c745, amount=15592000000000000000 ) => ( True )
    File 1 of 2: Share
    pragma solidity ^0.6.0;
    //pragma experimental ABIEncoderV2;
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    import './lib/Safe112.sol';
    import './owner/Operator.sol';
    import './utils/ContractGuard.sol';
    import './interfaces/IBasisAsset.sol';
    contract Boardroom is ContractGuard, Operator {
        using SafeERC20 for IERC20;
        using Address for address;
        using SafeMath for uint256;
        using Safe112 for uint112;
        /* ========== DATA STRUCTURES ========== */
        struct Boardseat {
            uint256 appointmentTime;
            uint256 shares;
        }
        struct BoardSnapshot {
            uint256 timestamp;
            uint256 rewardReceived;
            uint256 totalShares;
        }
        /* ========== STATE VARIABLES ========== */
        IERC20 private cash;
        IERC20 private share;
        mapping(address => Boardseat) private directors;
        BoardSnapshot[] private boardHistory;
        /* ========== CONSTRUCTOR ========== */
        constructor(IERC20 _cash, IERC20 _share) public {
            cash = _cash;
            share = _share;
            BoardSnapshot memory genesisSnapshot = BoardSnapshot(now, 0, 0);
            boardHistory.push(genesisSnapshot);
        }
        /* ========== Modifiers =============== */
        modifier directorExists {
            require(
                directors[msg.sender].shares > 0,
                'Boardroom: The director does not exist'
            );
            _;
        }
        /* ========== VIEW FUNCTIONS ========== */
        function totalShare() public view returns (uint256) {
            return boardHistory[boardHistory.length.sub(1)].totalShares;
        }
        function getShareOf(address director) public view returns (uint256) {
            return directors[director].shares;
        }
        function getAppointmentTimeOf(address director)
            public
            view
            returns (uint256)
        {
            return directors[director].appointmentTime;
        }
        function getCashEarningsOf(address director) public view returns (uint256) {
            uint256 totalRewards = 0;
            if (getShareOf(director) <= 0) {
                return totalRewards;
            }
            for (uint256 i = boardHistory.length; i > 0; i = i.sub(1)) {
                BoardSnapshot memory snapshot = boardHistory[i.sub(1)];
                if (snapshot.timestamp < getAppointmentTimeOf(director)) {
                    break;
                }
                uint256 snapshotRewards = snapshot
                    .rewardReceived
                    .mul(getShareOf(director))
                    .div(snapshot.totalShares);
                totalRewards = totalRewards.add(snapshotRewards);
            }
            return totalRewards;
        }
        /* ========== MUTATIVE FUNCTIONS ========== */
        function claimDividends() public onlyOneBlock {
            uint256 totalRewards = getCashEarningsOf(msg.sender);
            directors[msg.sender].appointmentTime = now;
            if (totalRewards > 0) {
                cash.safeTransfer(msg.sender, totalRewards);
                emit RewardPaid(msg.sender, totalRewards);
            }
        }
        function stake(uint256 amount) external {
            require(amount > 0, 'Boardroom: Cannot stake 0');
            // Claim all outstanding dividends before making state changes
            claimDividends();
            // Update director's boardseat
            Boardseat memory director = directors[msg.sender];
            director.shares = director.shares.add(amount);
            directors[msg.sender] = director;
            // Update latest snapshot
            uint256 snapshotIndex = boardHistory.length.sub(1);
            boardHistory[snapshotIndex].totalShares = totalShare().add(amount);
            share.safeTransferFrom(msg.sender, address(this), amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount) public directorExists {
            require(amount > 0, 'Boardroom: Cannot withdraw 0');
            // Claim all outstanding dividends before making state changes
            claimDividends();
            // Update director's boardseat
            uint256 directorShare = getShareOf(msg.sender);
            require(
                directorShare >= amount,
                'Boardroom: withdraw request greater than staked amount'
            );
            directors[msg.sender].shares = directorShare.sub(amount);
            // Update latest snapshot
            uint256 snapshotIndex = boardHistory.length.sub(1);
            boardHistory[snapshotIndex].totalShares = totalShare().sub(amount);
            share.safeTransfer(msg.sender, amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(getShareOf(msg.sender));
        }
        function allocateSeigniorage(uint256 amount)
            external
            onlyOneBlock
            onlyOperator
        {
            require(amount > 0, 'Boardroom: Cannot allocate 0');
            // Create & add new snapshot
            BoardSnapshot memory newSnapshot = BoardSnapshot({
                timestamp: now,
                rewardReceived: amount,
                totalShares: totalShare()
            });
            boardHistory.push(newSnapshot);
            cash.safeTransferFrom(msg.sender, address(this), amount);
            emit RewardAdded(msg.sender, amount);
        }
        /* ========== EVENTS ========== */
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        event RewardAdded(address indexed user, uint256 reward);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            // solhint-disable-next-line max-line-length
            require((value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).add(value);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) { // Return data is optional
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    pragma solidity ^0.6.0;
    library Safe112 {
        function add(uint112 a, uint112 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, 'Safe112: addition overflow');
            return c;
        }
        function sub(uint112 a, uint112 b) internal pure returns (uint256) {
            return sub(a, b, 'Safe112: subtraction overflow');
        }
        function sub(
            uint112 a,
            uint112 b,
            string memory errorMessage
        ) internal pure returns (uint112) {
            require(b <= a, errorMessage);
            uint112 c = a - b;
            return c;
        }
        function mul(uint112 a, uint112 b) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            uint256 c = a * b;
            require(c / a == b, 'Safe112: multiplication overflow');
            return c;
        }
        function div(uint112 a, uint112 b) internal pure returns (uint256) {
            return div(a, b, 'Safe112: division by zero');
        }
        function div(
            uint112 a,
            uint112 b,
            string memory errorMessage
        ) internal pure returns (uint112) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0, errorMessage);
            uint112 c = a / b;
            return c;
        }
        function mod(uint112 a, uint112 b) internal pure returns (uint256) {
            return mod(a, b, 'Safe112: modulo by zero');
        }
        function mod(
            uint112 a,
            uint112 b,
            string memory errorMessage
        ) internal pure returns (uint112) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/GSN/Context.sol';
    import '@openzeppelin/contracts/access/Ownable.sol';
    contract Operator is Context, Ownable {
        address private _operator;
        event OperatorTransferred(
            address indexed previousOperator,
            address indexed newOperator
        );
        constructor() internal {
            _operator = _msgSender();
            emit OperatorTransferred(address(0), _operator);
        }
        function operator() public view returns (address) {
            return _operator;
        }
        modifier onlyOperator() {
            require(
                _operator == msg.sender,
                'operator: caller is not the operator'
            );
            _;
        }
        function isOperator() public view returns (bool) {
            return _msgSender() == _operator;
        }
        function transferOperator(address newOperator_) public onlyOwner {
            _transferOperator(newOperator_);
        }
        function _transferOperator(address newOperator_) internal {
            require(
                newOperator_ != address(0),
                'operator: zero address given for new operator'
            );
            emit OperatorTransferred(address(0), newOperator_);
            _operator = newOperator_;
        }
    }
    pragma solidity ^0.6.12;
    contract ContractGuard {
        mapping(uint256 => mapping(address => bool)) private _status;
        modifier onlyOneBlock() {
            require(
                !_status[block.number][tx.origin],
                'ContractGuard: one block, one function'
            );
            _;
            _status[block.number][tx.origin] = true;
        }
    }
    pragma solidity ^0.6.0;
    interface IBasisAsset {
        function mint(address recipient, uint256 amount) external returns (bool);
        function burn(uint256 amount) external;
        function burnFrom(address from, uint256 amount) external;
        function isOperator() external returns (bool);
        function operator() external view returns (address);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
            return c;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) {
                return 0;
            }
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            return c;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts with custom message when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.2;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies in extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
            (bool success, ) = recipient.call{ value: amount }("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain`call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
            return _functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            return _functionCallWithValue(target, data, value, errorMessage);
        }
        function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
            require(isContract(target), "Address: call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with GSN meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address payable) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import "../GSN/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () internal {
            address msgSender = _msgSender();
            _owner = msgSender;
            emit OwnershipTransferred(address(0), msgSender);
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(_owner == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    import '../Treasury.sol';
    import '../Boardroom.sol';
    import '../owner/Operator.sol';
    contract Tester is Operator {
        Treasury public treasury;
        Boardroom public boardroom;
        constructor(address _treasury, address _boardroom) public {
            treasury = Treasury(_treasury);
            boardroom = Boardroom(_boardroom);
        }
        event OKOnlyOperator(address caller);
        function OnlyOperator() public onlyOperator {
            emit OKOnlyOperator(msg.sender);
        }
        function actionTreasury() public {
            treasury.allocateSeigniorage();
            treasury.allocateSeigniorage(); // should revert
        }
        function actionBoardroom(address share, uint256 amount) public {
            IERC20(share).approve(address(boardroom), amount);
            boardroom.stake(amount);
            boardroom.withdraw(amount); // should revert
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
    import './lib/Babylonian.sol';
    import './lib/FixedPoint.sol';
    import './lib/Safe112.sol';
    import './owner/Operator.sol';
    import './utils/ContractGuard.sol';
    import './interfaces/IBasisAsset.sol';
    import './interfaces/IOracle.sol';
    import './interfaces/IBoardroom.sol';
    /**
     * @title Basis Cash Treasury contract
     * @notice Monetary policy logic to adjust supplies of basis cash assets
     * @author Summer Smith & Rick Sanchez
     */
    contract Treasury is ContractGuard, Operator {
        using FixedPoint for *;
        using SafeERC20 for IERC20;
        using Address for address;
        using SafeMath for uint256;
        using Safe112 for uint112;
        /* ========= CONSTANT VARIABLES ======== */
        uint256 public constant allocationDelay = 1 days;
        /* ========== STATE VARIABLES ========== */
        address private cash;
        address private bond;
        address private share;
        address private boardroom;
        IOracle private cashOracle;
        bool private migrated = false;
        uint256 private seigniorageSaved = 0;
        uint256 public startTime;
        uint256 public cashPriceCeiling;
        uint256 public cashPriceOne;
        uint256 private bondDepletionFloor;
        uint256 private lastAllocated;
        /* ========== CONSTRUCTOR ========== */
        constructor(
            address _cash,
            address _bond,
            address _share,
            address _cashOracle,
            address _boardroom,
            uint256 _startTime
        ) public {
            cash = _cash;
            bond = _bond;
            share = _share;
            cashOracle = IOracle(_cashOracle);
            boardroom = _boardroom;
            startTime = _startTime;
            cashPriceOne = 10**18;
            cashPriceCeiling = uint256(105).mul(cashPriceOne).div(10**2);
            bondDepletionFloor = uint256(1000).mul(cashPriceOne);
            lastAllocated = now;
        }
        /* ========== MODIFIER ========== */
        modifier checkMigration {
            require(!migrated, 'Treasury: this contract has been migrated');
            _;
        }
        modifier checkOperator {
            require(
                IBasisAsset(cash).operator() == address(this),
                'Treasury: this contract is not the operator of the basis cash contract'
            );
            require(
                IBasisAsset(bond).operator() == address(this),
                'Treasury: this contract is not the operator of the basis bond contract'
            );
            require(
                Operator(boardroom).operator() == address(this),
                'Treasury: this contract is not the operator of the boardroom contract'
            );
            _;
        }
        /* ========== VIEW FUNCTIONS ========== */
        function getCashPrice() public view returns (uint256 cashPrice) {
            try cashOracle.consult(cash, 1e18) returns (uint256 price) {
                return price;
            } catch {
                revert('Treasury: failed to consult cash price from the oracle');
            }
        }
        /* ========== GOVERNANCE ========== */
        function migrate(address target) public onlyOperator checkMigration {
            require(block.timestamp >= startTime, 'Treasury: not started yet');
            // cash
            Operator(cash).transferOperator(target);
            Operator(cash).transferOwnership(target);
            IERC20(cash).transfer(target, IERC20(cash).balanceOf(address(this)));
            // bond
            Operator(bond).transferOperator(target);
            Operator(bond).transferOwnership(target);
            IERC20(bond).transfer(target, IERC20(bond).balanceOf(address(this)));
            // share
            Operator(share).transferOperator(target);
            Operator(share).transferOwnership(target);
            IERC20(share).transfer(target, IERC20(share).balanceOf(address(this)));
            migrated = true;
            emit Migration(target);
        }
        /* ========== MUTABLE FUNCTIONS ========== */
        function _getCashPrice() internal returns (uint256 cashPrice) {
            cashPrice = getCashPrice();
            try cashOracle.update()  {} catch {
                revert('Treasury: failed to update cash oracle');
            }
        }
        function _allocateSeigniorage(uint256 cashPrice)
            internal
            onlyOneBlock
            checkOperator
            checkMigration
            returns (bool, string memory)
        {
            if (now.sub(lastAllocated) < allocationDelay) {
                return (false, 'Treasury: a day has not passed yet');
            }
            if (block.timestamp < startTime) {
                return (false, 'Treasury: not started yet');
            }
            if (cashPrice <= cashPriceCeiling) {
                return (false, 'Treasury: there is no seigniorage to be allocated');
            }
            uint256 cashSupply = IERC20(cash).totalSupply();
            uint256 percentage = cashPrice.sub(cashPriceOne);
            uint256 seigniorage = cashSupply.mul(percentage).div(1e18);
            if (seigniorageSaved > bondDepletionFloor) {
                IBasisAsset(cash).mint(address(this), seigniorage);
                IERC20(cash).safeApprove(boardroom, seigniorage);
                IBoardroom(boardroom).allocateSeigniorage(seigniorage);
                emit BoardroomFunded(now, seigniorage);
            } else {
                seigniorageSaved = seigniorageSaved.add(seigniorage);
                IBasisAsset(cash).mint(address(this), seigniorage);
                emit TreasuryFunded(now, seigniorage);
            }
            lastAllocated = now;
            return (true, 'Treasury: success');
        }
        function buyBonds(uint256 amount, uint256 targetPrice) external {
            require(amount > 0, 'Treasury: cannot purchase bonds with zero amount');
            uint256 cashPrice = _getCashPrice();
            require(cashPrice == targetPrice, 'Treasury: cash price moved');
            _allocateSeigniorage(cashPrice); // ignore returns
            uint256 bondPrice = cashPrice;
            IBasisAsset(cash).burnFrom(msg.sender, amount);
            IBasisAsset(bond).mint(msg.sender, amount.mul(1e18).div(bondPrice));
            emit BoughtBonds(msg.sender, amount);
        }
        function redeemBonds(uint256 amount, uint256 targetPrice) external {
            require(amount > 0, 'Treasury: cannot redeem bonds with zero amount');
            uint256 cashPrice = _getCashPrice();
            require(cashPrice == targetPrice, 'Treasury: cash price moved');
            _allocateSeigniorage(cashPrice); // ignore returns
            require(
                cashPrice > cashPriceCeiling,
                'Treasury: bond redemption failed; basis cash remains depegged.'
            );
            uint256 treasuryBalance = IERC20(cash).balanceOf(address(this));
            require(
                treasuryBalance >= amount,
                'Treasury: treasury has no more budget'
            );
            if (seigniorageSaved >= amount) {
                seigniorageSaved = seigniorageSaved.sub(amount);
            } else {
                seigniorageSaved = 0;
            }
            IBasisAsset(bond).burnFrom(msg.sender, amount);
            IERC20(cash).safeTransfer(msg.sender, amount);
            emit RedeemedBonds(msg.sender, amount);
        }
        function allocateSeigniorage() external {
            uint256 cashPrice = _getCashPrice();
            (bool result, string memory reason) = _allocateSeigniorage(cashPrice);
            require(result, reason);
        }
        event Migration(address indexed target);
        event RedeemedBonds(address indexed from, uint256 amount);
        event BoughtBonds(address indexed from, uint256 amount);
        event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
        event BoardroomFunded(uint256 timestamp, uint256 seigniorage);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor () internal {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and make it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    pragma solidity ^0.6.0;
    library Babylonian {
        function sqrt(uint256 y) internal pure returns (uint256 z) {
            if (y > 3) {
                z = y;
                uint256 x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
            // else z = 0
        }
    }
    pragma solidity ^0.6.0;
    import './Babylonian.sol';
    // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
    library FixedPoint {
        // range: [0, 2**112 - 1]
        // resolution: 1 / 2**112
        struct uq112x112 {
            uint224 _x;
        }
        // range: [0, 2**144 - 1]
        // resolution: 1 / 2**112
        struct uq144x112 {
            uint256 _x;
        }
        uint8 private constant RESOLUTION = 112;
        uint256 private constant Q112 = uint256(1) << RESOLUTION;
        uint256 private constant Q224 = Q112 << RESOLUTION;
        // encode a uint112 as a UQ112x112
        function encode(uint112 x) internal pure returns (uq112x112 memory) {
            return uq112x112(uint224(x) << RESOLUTION);
        }
        // encodes a uint144 as a UQ144x112
        function encode144(uint144 x) internal pure returns (uq144x112 memory) {
            return uq144x112(uint256(x) << RESOLUTION);
        }
        // divide a UQ112x112 by a uint112, returning a UQ112x112
        function div(uq112x112 memory self, uint112 x)
            internal
            pure
            returns (uq112x112 memory)
        {
            require(x != 0, 'FixedPoint: DIV_BY_ZERO');
            return uq112x112(self._x / uint224(x));
        }
        // multiply a UQ112x112 by a uint, returning a UQ144x112
        // reverts on overflow
        function mul(uq112x112 memory self, uint256 y)
            internal
            pure
            returns (uq144x112 memory)
        {
            uint256 z;
            require(
                y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x),
                'FixedPoint: MULTIPLICATION_OVERFLOW'
            );
            return uq144x112(z);
        }
        // returns a UQ112x112 which represents the ratio of the numerator to the denominator
        // equivalent to encode(numerator).div(denominator)
        function fraction(uint112 numerator, uint112 denominator)
            internal
            pure
            returns (uq112x112 memory)
        {
            require(denominator > 0, 'FixedPoint: DIV_BY_ZERO');
            return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
        }
        // decode a UQ112x112 into a uint112 by truncating after the radix point
        function decode(uq112x112 memory self) internal pure returns (uint112) {
            return uint112(self._x >> RESOLUTION);
        }
        // decode a UQ144x112 into a uint144 by truncating after the radix point
        function decode144(uq144x112 memory self) internal pure returns (uint144) {
            return uint144(self._x >> RESOLUTION);
        }
        // take the reciprocal of a UQ112x112
        function reciprocal(uq112x112 memory self)
            internal
            pure
            returns (uq112x112 memory)
        {
            require(self._x != 0, 'FixedPoint: ZERO_RECIPROCAL');
            return uq112x112(uint224(Q224 / self._x));
        }
        // square root of a UQ112x112
        function sqrt(uq112x112 memory self)
            internal
            pure
            returns (uq112x112 memory)
        {
            return uq112x112(uint224(Babylonian.sqrt(uint256(self._x)) << 56));
        }
    }
    pragma solidity ^0.6.0;
    interface IOracle {
        function update() external;
        function consult(address token, uint256 amountIn)
            external
            view
            returns (uint256 amountOut);
        // function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp);
    }
    pragma solidity ^0.6.0;
    interface IBoardroom {
        function allocateSeigniorage(uint256 amount) external;
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/math/SafeMath.sol';
    import './lib/Babylonian.sol';
    import './lib/FixedPoint.sol';
    import './lib/UniswapV2Library.sol';
    import './lib/UniswapV2OracleLibrary.sol';
    import './interfaces/IUniswapV2Pair.sol';
    import './interfaces/IUniswapV2Factory.sol';
    // fixed window oracle that recomputes the average price for the entire period once every period
    // note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
    contract Oracle {
        using FixedPoint for *;
        uint256 public constant PERIOD = 10 minutes;
        IUniswapV2Pair public pair;
        address public token0;
        address public token1;
        uint256 public price0CumulativeLast;
        uint256 public price1CumulativeLast;
        uint32 public blockTimestampLast;
        FixedPoint.uq112x112 public price0Average;
        FixedPoint.uq112x112 public price1Average;
        constructor(
            address factory,
            address tokenA,
            address tokenB
        ) public {
            IUniswapV2Pair _pair = IUniswapV2Pair(
                UniswapV2Library.pairFor(factory, tokenA, tokenB)
            );
            pair = _pair;
            token0 = _pair.token0();
            token1 = _pair.token1();
            price0CumulativeLast = _pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
            price1CumulativeLast = _pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
            uint112 reserve0;
            uint112 reserve1;
            (reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
            require(reserve0 != 0 && reserve1 != 0, 'Oracle: NO_RESERVES'); // ensure that there's liquidity in the pair
        }
        /** @dev Updates 1-day EMA price from Uniswap.  */
        function update() external {
            (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            ) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
            if (timeElapsed < PERIOD) {
                // doesn't need to be updated, since a minimum period is not elapsed yet
                return;
            }
            // overflow is desired, casting never truncates
            // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
            price0Average = FixedPoint.uq112x112(
                uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)
            );
            price1Average = FixedPoint.uq112x112(
                uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)
            );
            price0CumulativeLast = price0Cumulative;
            price1CumulativeLast = price1Cumulative;
            blockTimestampLast = blockTimestamp;
            emit Updated(price0Cumulative, price1Cumulative);
        }
        // note this will always return 0 before update has been called successfully for the first time.
        function consult(address token, uint256 amountIn)
            external
            view
            returns (uint144 amountOut)
        {
            if (token == token0) {
                amountOut = price0Average.mul(amountIn).decode144();
            } else {
                require(token == token1, 'Oracle: INVALID_TOKEN');
                amountOut = price1Average.mul(amountIn).decode144();
            }
        }
        function pairFor(
            address factory,
            address tokenA,
            address tokenB
        ) external pure returns (address lpt) {
            return UniswapV2Library.pairFor(factory, tokenA, tokenB);
        }
        event Updated(uint256 price0CumulativeLast, uint256 price1CumulativeLast);
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/math/SafeMath.sol';
    import '../interfaces/IUniswapV2Pair.sol';
    library UniswapV2Library {
        using SafeMath for uint256;
        // returns sorted token addresses, used to handle return values from pairs sorted in this order
        function sortTokens(address tokenA, address tokenB)
            internal
            pure
            returns (address token0, address token1)
        {
            require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
            (token0, token1) = tokenA < tokenB
                ? (tokenA, tokenB)
                : (tokenB, tokenA);
            require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
        }
        // calculates the CREATE2 address for a pair without making any external calls
        function pairFor(
            address factory,
            address tokenA,
            address tokenB
        ) internal pure returns (address pair) {
            (address token0, address token1) = sortTokens(tokenA, tokenB);
            pair = address(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex'ff',
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                        )
                    )
                )
            );
        }
        // fetches and sorts the reserves for a pair
        function getReserves(
            address factory,
            address tokenA,
            address tokenB
        ) internal view returns (uint256 reserveA, uint256 reserveB) {
            (address token0, ) = sortTokens(tokenA, tokenB);
            (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
                pairFor(factory, tokenA, tokenB)
            )
                .getReserves();
            (reserveA, reserveB) = tokenA == token0
                ? (reserve0, reserve1)
                : (reserve1, reserve0);
        }
        // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
        function quote(
            uint256 amountA,
            uint256 reserveA,
            uint256 reserveB
        ) internal pure returns (uint256 amountB) {
            require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
            require(
                reserveA > 0 && reserveB > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            amountB = amountA.mul(reserveB) / reserveA;
        }
        // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
        function getAmountOut(
            uint256 amountIn,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountOut) {
            require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 amountInWithFee = amountIn.mul(997);
            uint256 numerator = amountInWithFee.mul(reserveOut);
            uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
            amountOut = numerator / denominator;
        }
        // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
        function getAmountIn(
            uint256 amountOut,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountIn) {
            require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 numerator = reserveIn.mul(amountOut).mul(1000);
            uint256 denominator = reserveOut.sub(amountOut).mul(997);
            amountIn = (numerator / denominator).add(1);
        }
        // performs chained getAmountOut calculations on any number of pairs
        function getAmountsOut(
            address factory,
            uint256 amountIn,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[0] = amountIn;
            for (uint256 i; i < path.length - 1; i++) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i],
                    path[i + 1]
                );
                amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
            }
        }
        // performs chained getAmountIn calculations on any number of pairs
        function getAmountsIn(
            address factory,
            uint256 amountOut,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[amounts.length - 1] = amountOut;
            for (uint256 i = path.length - 1; i > 0; i--) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i - 1],
                    path[i]
                );
                amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
            }
        }
    }
    pragma solidity ^0.6.0;
    import './FixedPoint.sol';
    import '../interfaces/IUniswapV2Pair.sol';
    // library with helper methods for oracles that are concerned with computing average prices
    library UniswapV2OracleLibrary {
        using FixedPoint for *;
        // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
        function currentBlockTimestamp() internal view returns (uint32) {
            return uint32(block.timestamp % 2**32);
        }
        // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
        function currentCumulativePrices(address pair)
            internal
            view
            returns (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            )
        {
            blockTimestamp = currentBlockTimestamp();
            price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
            price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
            // if time has elapsed since the last update on the pair, mock the accumulated price values
            (
                uint112 reserve0,
                uint112 reserve1,
                uint32 blockTimestampLast
            ) = IUniswapV2Pair(pair).getReserves();
            if (blockTimestampLast != blockTimestamp) {
                // subtraction overflow is desired
                uint32 timeElapsed = blockTimestamp - blockTimestampLast;
                // addition overflow is desired
                // counterfactual
                price0Cumulative +=
                    uint256(FixedPoint.fraction(reserve1, reserve0)._x) *
                    timeElapsed;
                // counterfactual
                price1Cumulative +=
                    uint256(FixedPoint.fraction(reserve0, reserve1)._x) *
                    timeElapsed;
            }
        }
    }
    pragma solidity ^0.6.0;
    interface IUniswapV2Pair {
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
        event Transfer(address indexed from, address indexed to, uint256 value);
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint256);
        function balanceOf(address owner) external view returns (uint256);
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
        function approve(address spender, uint256 value) external returns (bool);
        function transfer(address to, uint256 value) external returns (bool);
        function transferFrom(
            address from,
            address to,
            uint256 value
        ) external returns (bool);
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint256);
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        event Mint(address indexed sender, uint256 amount0, uint256 amount1);
        event Burn(
            address indexed sender,
            uint256 amount0,
            uint256 amount1,
            address indexed to
        );
        event Swap(
            address indexed sender,
            uint256 amount0In,
            uint256 amount1In,
            uint256 amount0Out,
            uint256 amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
        function MINIMUM_LIQUIDITY() external pure returns (uint256);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves()
            external
            view
            returns (
                uint112 reserve0,
                uint112 reserve1,
                uint32 blockTimestampLast
            );
        function price0CumulativeLast() external view returns (uint256);
        function price1CumulativeLast() external view returns (uint256);
        function kLast() external view returns (uint256);
        function mint(address to) external returns (uint256 liquidity);
        function burn(address to)
            external
            returns (uint256 amount0, uint256 amount1);
        function swap(
            uint256 amount0Out,
            uint256 amount1Out,
            address to,
            bytes calldata data
        ) external;
        function skim(address to) external;
        function sync() external;
        function initialize(address, address) external;
    }
    pragma solidity ^0.6.0;
    interface IUniswapV2Factory {
        event PairCreated(
            address indexed token0,
            address indexed token1,
            address pair,
            uint256
        );
        function getPair(address tokenA, address tokenB)
            external
            view
            returns (address pair);
        function allPairs(uint256) external view returns (address pair);
        function allPairsLength() external view returns (uint256);
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
        function createPair(address tokenA, address tokenB)
            external
            returns (address pair);
    }
    pragma solidity ^0.6.0;
    /*
     * Copyright 2020 Compound Labs, Inc.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice,
     * this list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     * this list of conditions and the following disclaimer in the documentation
     * and/or other materials provided with the distribution.
     *
     * 3. Neither the name of the copyright holder nor the names of its contributors
     * may be used to endorse or promote products derived from this software without
     * specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    import '@openzeppelin/contracts/math/SafeMath.sol';
    contract Timelock {
        using SafeMath for uint256;
        event NewAdmin(address indexed newAdmin);
        event NewPendingAdmin(address indexed newPendingAdmin);
        event NewDelay(uint256 indexed newDelay);
        event CancelTransaction(
            bytes32 indexed txHash,
            address indexed target,
            uint256 value,
            string signature,
            bytes data,
            uint256 eta
        );
        event ExecuteTransaction(
            bytes32 indexed txHash,
            address indexed target,
            uint256 value,
            string signature,
            bytes data,
            uint256 eta
        );
        event QueueTransaction(
            bytes32 indexed txHash,
            address indexed target,
            uint256 value,
            string signature,
            bytes data,
            uint256 eta
        );
        uint256 public constant GRACE_PERIOD = 14 days;
        uint256 public constant MINIMUM_DELAY = 2 days;
        uint256 public constant MAXIMUM_DELAY = 30 days;
        address public admin;
        address public pendingAdmin;
        uint256 public delay;
        mapping(bytes32 => bool) public queuedTransactions;
        constructor(address admin_, uint256 delay_) public {
            require(
                delay_ >= MINIMUM_DELAY,
                'Timelock::constructor: Delay must exceed minimum delay.'
            );
            require(
                delay_ <= MAXIMUM_DELAY,
                'Timelock::setDelay: Delay must not exceed maximum delay.'
            );
            admin = admin_;
            delay = delay_;
        }
        receive() external payable {}
        function setDelay(uint256 delay_) public {
            require(
                msg.sender == address(this),
                'Timelock::setDelay: Call must come from Timelock.'
            );
            require(
                delay_ >= MINIMUM_DELAY,
                'Timelock::setDelay: Delay must exceed minimum delay.'
            );
            require(
                delay_ <= MAXIMUM_DELAY,
                'Timelock::setDelay: Delay must not exceed maximum delay.'
            );
            delay = delay_;
            emit NewDelay(delay);
        }
        function acceptAdmin() public {
            require(
                msg.sender == pendingAdmin,
                'Timelock::acceptAdmin: Call must come from pendingAdmin.'
            );
            admin = msg.sender;
            pendingAdmin = address(0);
            emit NewAdmin(admin);
        }
        function setPendingAdmin(address pendingAdmin_) public {
            require(
                msg.sender == address(this),
                'Timelock::setPendingAdmin: Call must come from Timelock.'
            );
            pendingAdmin = pendingAdmin_;
            emit NewPendingAdmin(pendingAdmin);
        }
        function queueTransaction(
            address target,
            uint256 value,
            string memory signature,
            bytes memory data,
            uint256 eta
        ) public returns (bytes32) {
            require(
                msg.sender == admin,
                'Timelock::queueTransaction: Call must come from admin.'
            );
            require(
                eta >= getBlockTimestamp().add(delay),
                'Timelock::queueTransaction: Estimated execution block must satisfy delay.'
            );
            bytes32 txHash = keccak256(
                abi.encode(target, value, signature, data, eta)
            );
            queuedTransactions[txHash] = true;
            emit QueueTransaction(txHash, target, value, signature, data, eta);
            return txHash;
        }
        function cancelTransaction(
            address target,
            uint256 value,
            string memory signature,
            bytes memory data,
            uint256 eta
        ) public {
            require(
                msg.sender == admin,
                'Timelock::cancelTransaction: Call must come from admin.'
            );
            bytes32 txHash = keccak256(
                abi.encode(target, value, signature, data, eta)
            );
            queuedTransactions[txHash] = false;
            emit CancelTransaction(txHash, target, value, signature, data, eta);
        }
        function executeTransaction(
            address target,
            uint256 value,
            string memory signature,
            bytes memory data,
            uint256 eta
        ) public payable returns (bytes memory) {
            require(
                msg.sender == admin,
                'Timelock::executeTransaction: Call must come from admin.'
            );
            bytes32 txHash = keccak256(
                abi.encode(target, value, signature, data, eta)
            );
            require(
                queuedTransactions[txHash],
                "Timelock::executeTransaction: Transaction hasn't been queued."
            );
            require(
                getBlockTimestamp() >= eta,
                "Timelock::executeTransaction: Transaction hasn't surpassed time lock."
            );
            require(
                getBlockTimestamp() <= eta.add(GRACE_PERIOD),
                'Timelock::executeTransaction: Transaction is stale.'
            );
            queuedTransactions[txHash] = false;
            bytes memory callData;
            if (bytes(signature).length == 0) {
                callData = data;
            } else {
                callData = abi.encodePacked(
                    bytes4(keccak256(bytes(signature))),
                    data
                );
            }
            // solium-disable-next-line security/no-call-value
            (bool success, bytes memory returnData) = target.call{value: value}(
                callData
            );
            require(
                success,
                'Timelock::executeTransaction: Transaction execution reverted.'
            );
            emit ExecuteTransaction(txHash, target, value, signature, data, eta);
            return returnData;
        }
        function getBlockTimestamp() internal view returns (uint256) {
            // solium-disable-next-line security/no-block-members
            return block.timestamp;
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/math/SafeMath.sol';
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    import '../interfaces/IDistributor.sol';
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract InitialShareDistributor is IDistributor {
        using SafeMath for uint256;
        event Distributed(address pool, uint256 cashAmount);
        bool public once = true;
        IERC20 public share;
        IRewardDistributionRecipient public daibacLPPool;
        uint256 public daibacInitialBalance;
        IRewardDistributionRecipient public daibasLPPool;
        uint256 public daibasInitialBalance;
        constructor(
            IERC20 _share,
            IRewardDistributionRecipient _daibacLPPool,
            uint256 _daibacInitialBalance,
            IRewardDistributionRecipient _daibasLPPool,
            uint256 _daibasInitialBalance
        ) public {
            share = _share;
            daibacLPPool = _daibacLPPool;
            daibacInitialBalance = _daibacInitialBalance;
            daibasLPPool = _daibasLPPool;
            daibasInitialBalance = _daibasInitialBalance;
        }
        function distribute() public override {
            require(
                once,
                'InitialShareDistributor: you cannot run this function twice'
            );
            share.transfer(address(daibacLPPool), daibacInitialBalance);
            daibacLPPool.notifyRewardAmount(daibacInitialBalance);
            emit Distributed(address(daibacLPPool), daibacInitialBalance);
            share.transfer(address(daibasLPPool), daibasInitialBalance);
            daibasLPPool.notifyRewardAmount(daibasInitialBalance);
            emit Distributed(address(daibasLPPool), daibasInitialBalance);
            once = false;
        }
    }
    pragma solidity ^0.6.0;
    interface IDistributor {
        function distribute() external;
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/access/Ownable.sol';
    abstract contract IRewardDistributionRecipient is Ownable {
        address public rewardDistribution;
        function notifyRewardAmount(uint256 reward) external virtual;
        modifier onlyRewardDistribution() {
            require(
                _msgSender() == rewardDistribution,
                'Caller is not reward distribution'
            );
            _;
        }
        function setRewardDistribution(address _rewardDistribution)
            external
            virtual
            onlyOwner
        {
            rewardDistribution = _rewardDistribution;
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    import '../token/LPTokenWrapper.sol';
    contract DAIBASLPTokenSharePool is
        LPTokenWrapper,
        IRewardDistributionRecipient
    {
        IERC20 public basisShare;
        uint256 public DURATION = 365 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisShare_,
            address lptoken_,
            uint256 starttime_
        ) public {
            basisShare = IERC20(basisShare_);
            lpt = IERC20(lptoken_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(
                block.timestamp >= starttime,
                'DAIBASLPTokenSharePool: not start'
            );
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'DAIBASLPTokenSharePool: Cannot stake 0');
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'DAIBASLPTokenSharePool: Cannot withdraw 0');
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisShare.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow, so we distribute
            return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/math/SafeMath.sol';
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    contract LPTokenWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public lpt;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            lpt.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            lpt.safeTransfer(msg.sender, amount);
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    import '../token/LPTokenWrapper.sol';
    contract DAIBACLPTokenSharePool is
        LPTokenWrapper,
        IRewardDistributionRecipient
    {
        IERC20 public basisShare;
        uint256 public constant DURATION = 30 days;
        uint256 public initreward = 18479995 * 10**16; // 184,799.95 Shares
        uint256 public starttime; // starttime TBD
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisShare_,
            address lptoken_,
            uint256 starttime_
        ) public {
            basisShare = IERC20(basisShare_);
            lpt = IERC20(lptoken_);
            starttime = starttime_;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkhalve
            checkStart
        {
            require(amount > 0, 'Cannot stake 0');
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkhalve
            checkStart
        {
            require(amount > 0, 'Cannot withdraw 0');
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkhalve checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisShare.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        modifier checkhalve() {
            if (block.timestamp >= periodFinish) {
                initreward = initreward.mul(75).div(100);
                rewardRate = initreward.div(DURATION);
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(initreward);
            }
            _;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'not start');
            _;
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = initreward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract yCRVWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public ycrv;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public virtual view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public virtual view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            ycrv.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            ycrv.safeTransfer(msg.sender, amount);
        }
    }
    contract BACyCRVPool is yCRVWrapper, IRewardDistributionRecipient {
        IERC20 public basisCash;
        uint256 public DURATION = 5 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        mapping(address => uint256) public deposits;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisCash_,
            address ycrv_,
            uint256 starttime_
        ) public {
            basisCash = IERC20(basisCash_);
            ycrv = IERC20(ycrv_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'BACyCRVPool: not start');
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACyCRVPool: Cannot stake 0');
            uint256 newDeposit = deposits[msg.sender].add(amount);
            require(
                newDeposit <= 20000e18,
                'BACyCRVPool: deposit amount exceeds maximum 20000'
            );
            deposits[msg.sender] = newDeposit;
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACyCRVPool: Cannot withdraw 0');
            deposits[msg.sender] = deposits[msg.sender].sub(amount);
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisCash.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    import '../distribution/BACDAIPool.sol';
    import '../distribution/BACSUSDPool.sol';
    import '../distribution/BACUSDCPool.sol';
    import '../distribution/BACUSDTPool.sol';
    import '../distribution/BACyCRVPool.sol';
    import '../interfaces/IDistributor.sol';
    contract InitialCashDistributor is IDistributor {
        using SafeMath for uint256;
        event Distributed(address pool, uint256 cashAmount);
        bool public once = true;
        IERC20 public cash;
        IRewardDistributionRecipient[] public pools;
        uint256 public totalInitialBalance;
        constructor(
            IERC20 _cash,
            IRewardDistributionRecipient[] memory _pools,
            uint256 _totalInitialBalance
        ) public {
            require(_pools.length != 0, 'a list of BAC pools are required');
            cash = _cash;
            pools = _pools;
            totalInitialBalance = _totalInitialBalance;
        }
        function distribute() public override {
            require(
                once,
                'InitialCashDistributor: you cannot run this function twice'
            );
            for (uint256 i = 0; i < pools.length; i++) {
                uint256 amount = totalInitialBalance.div(pools.length);
                cash.transfer(address(pools[i]), amount);
                pools[i].notifyRewardAmount(amount);
                emit Distributed(address(pools[i]), amount);
            }
            once = false;
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract DAIWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public dai;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            dai.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            dai.safeTransfer(msg.sender, amount);
        }
    }
    contract BACDAIPool is DAIWrapper, IRewardDistributionRecipient {
        IERC20 public basisCash;
        uint256 public DURATION = 5 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        mapping(address => uint256) public deposits;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisCash_,
            address dai_,
            uint256 starttime_
        ) public {
            basisCash = IERC20(basisCash_);
            dai = IERC20(dai_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'BACDAIPool: not start');
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACDAIPool: Cannot stake 0');
            uint256 newDeposit = deposits[msg.sender].add(amount);
            require(
                newDeposit <= 20000e18,
                'BACDAIPool: deposit amount exceeds maximum 20000'
            );
            deposits[msg.sender] = newDeposit;
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACDAIPool: Cannot withdraw 0');
            deposits[msg.sender] = deposits[msg.sender].sub(amount);
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisCash.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract SUSDWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public SUSD;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            SUSD.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            SUSD.safeTransfer(msg.sender, amount);
        }
    }
    contract BACSUSDPool is SUSDWrapper, IRewardDistributionRecipient {
        IERC20 public basisCash;
        uint256 public DURATION = 5 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        mapping(address => uint256) public deposits;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisCash_,
            address susd_,
            uint256 starttime_
        ) public {
            basisCash = IERC20(basisCash_);
            SUSD = IERC20(susd_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'BACSUSDPool: not start');
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACSUSDPool: Cannot stake 0');
            uint256 newDeposit = deposits[msg.sender].add(amount);
            require(
                newDeposit <= 20000e18,
                'BACSUSDPool: deposit amount exceeds maximum 20000'
            );
            deposits[msg.sender] = newDeposit;
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACSUSDPool: Cannot withdraw 0');
            deposits[msg.sender] = deposits[msg.sender].sub(amount);
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisCash.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract USDCWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public usdc;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            usdc.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            usdc.safeTransfer(msg.sender, amount);
        }
    }
    contract BACUSDCPool is USDCWrapper, IRewardDistributionRecipient {
        IERC20 public basisCash;
        uint256 public DURATION = 5 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        mapping(address => uint256) public deposits;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisCash_,
            address usdc_,
            uint256 starttime_
        ) public {
            basisCash = IERC20(basisCash_);
            usdc = IERC20(usdc_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'BACUSDCPool: not start');
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACUSDCPool: Cannot stake 0');
            uint256 newDeposit = deposits[msg.sender].add(amount);
            require(
                newDeposit <= 20000e6,
                'BACUSDCPool: deposit amount exceeds maximum 20000'
            );
            deposits[msg.sender] = newDeposit;
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACUSDCPool: Cannot withdraw 0');
            deposits[msg.sender] = deposits[msg.sender].sub(amount);
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisCash.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    /**
     *Submitted for verification at Etherscan.io on 2020-07-17
     */
    /*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\\ \\ / // // _ \\/ __// _ \\/ -_)/ __// / \\ \\ /
    /___/ \\_, //_//_/\\__//_//_/\\__/ \\__//_/ /_\\_\\
         /___/
    * Synthetix: BASISCASHRewards.sol
    *
    * Docs: https://docs.synthetix.io/
    *
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2020 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    // File: @openzeppelin/contracts/math/Math.sol
    import '@openzeppelin/contracts/math/Math.sol';
    // File: @openzeppelin/contracts/math/SafeMath.sol
    import '@openzeppelin/contracts/math/SafeMath.sol';
    // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
    import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
    // File: @openzeppelin/contracts/utils/Address.sol
    import '@openzeppelin/contracts/utils/Address.sol';
    // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
    import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
    // File: contracts/IRewardDistributionRecipient.sol
    import '../interfaces/IRewardDistributionRecipient.sol';
    contract USDTWrapper {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        IERC20 public usdt;
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
        function stake(uint256 amount) public virtual {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            usdt.safeTransferFrom(msg.sender, address(this), amount);
        }
        function withdraw(uint256 amount) public virtual {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            usdt.safeTransfer(msg.sender, amount);
        }
    }
    contract BACUSDTPool is USDTWrapper, IRewardDistributionRecipient {
        IERC20 public basisCash;
        uint256 public DURATION = 5 days;
        uint256 public starttime;
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        mapping(address => uint256) public deposits;
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
        constructor(
            address basisCash_,
            address usdt_,
            uint256 starttime_
        ) public {
            basisCash = IERC20(basisCash_);
            usdt = IERC20(usdt_);
            starttime = starttime_;
        }
        modifier checkStart() {
            require(block.timestamp >= starttime, 'BACUSDTPool: not start');
            _;
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
        // stake visibility is public as overriding LPTokenWrapper's stake() function
        function stake(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACUSDTPool: Cannot stake 0');
            uint256 newDeposit = deposits[msg.sender].add(amount);
            require(
                newDeposit <= 20000e6,
                'BACUSDTPool: deposit amount exceeds maximum 20000'
            );
            deposits[msg.sender] = newDeposit;
            super.stake(amount);
            emit Staked(msg.sender, amount);
        }
        function withdraw(uint256 amount)
            public
            override
            updateReward(msg.sender)
            checkStart
        {
            require(amount > 0, 'BACUSDTPool: Cannot withdraw 0');
            deposits[msg.sender] = deposits[msg.sender].sub(amount);
            super.withdraw(amount);
            emit Withdrawn(msg.sender, amount);
        }
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
        function getReward() public updateReward(msg.sender) checkStart {
            uint256 reward = earned(msg.sender);
            if (reward > 0) {
                rewards[msg.sender] = 0;
                basisCash.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function notifyRewardAmount(uint256 reward)
            external
            override
            onlyRewardDistribution
            updateReward(address(0))
        {
            if (block.timestamp > starttime) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(DURATION);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(DURATION);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(DURATION);
                emit RewardAdded(reward);
            } else {
                rewardRate = reward.div(DURATION);
                lastUpdateTime = starttime;
                periodFinish = starttime.add(DURATION);
                emit RewardAdded(reward);
            }
        }
    }
    pragma solidity ^0.6.0;
    import './interfaces/IDistributor.sol';
    contract Distributor {
        IDistributor[] public distributors;
        constructor(IDistributor[] memory _distributors) public {
            distributors = _distributors;
        }
        function distribute() public {
            for (uint256 i = 0; i < distributors.length; i++) {
                distributors[i].distribute();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import "../../GSN/Context.sol";
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20 {
        using SafeMath for uint256;
        using Address for address;
        mapping (address => uint256) private _balances;
        mapping (address => mapping (address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        uint8 private _decimals;
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name, string memory symbol) public {
            _name = name;
            _symbol = symbol;
            _decimals = 18;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view returns (uint8) {
            return _decimals;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20};
         *
         * Requirements:
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
            return true;
        }
        /**
         * @dev Moves tokens `amount` from `sender` to `recipient`.
         *
         * This is internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(address sender, address recipient, uint256 amount) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
            _balances[recipient] = _balances[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements
         *
         * - `to` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Transfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
            _totalSupply = _totalSupply.sub(amount);
            emit Transfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Sets {decimals} to a value other than the default one of 18.
         *
         * WARNING: This function should only be called from the constructor. Most
         * applications that interact with token contracts will not expect
         * {decimals} to ever change, and may work incorrectly if it does.
         */
        function _setupDecimals(uint8 decimals_) internal {
            _decimals = decimals_;
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import "../../GSN/Context.sol";
    import "./ERC20.sol";
    /**
     * @dev Extension of {ERC20} that allows token holders to destroy both their own
     * tokens and those that they have an allowance for, in a way that can be
     * recognized off-chain (via event analysis).
     */
    abstract contract ERC20Burnable is Context, ERC20 {
        /**
         * @dev Destroys `amount` tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 amount) public virtual {
            _burn(_msgSender(), amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, deducting from the caller's
         * allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `amount`.
         */
        function burnFrom(address account, uint256 amount) public virtual {
            uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
            _approve(account, _msgSender(), decreasedAllowance);
            _burn(account, amount);
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol';
    import '../owner/Operator.sol';
    contract MockDai is ERC20Burnable, Operator {
        /**
         * @notice Constructs the Basis Cash ERC-20 contract.
         */
        constructor() public ERC20('DAI', 'DAI') {
            _mint(msg.sender, 10000 * 10**18);
        }
        /**
         * @notice Operator mints dino cash to a recipient
         * @param recipient_ The address of recipient
         * @param amount_ The amount of dino cash to mint to
         * @return whether the process has been done
         */
        function mint(address recipient_, uint256 amount_)
            public
            onlyOperator
            returns (bool)
        {
            uint256 balanceBefore = balanceOf(recipient_);
            _mint(recipient_, amount_);
            uint256 balanceAfter = balanceOf(recipient_);
            return balanceAfter > balanceBefore;
        }
    }
    pragma solidity ^0.6.0;
    import './owner/Operator.sol';
    import '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol';
    contract Share is ERC20Burnable, Operator {
        constructor() public ERC20('BAS', 'BAS') {
            // Mints 1 Basis Share to contract creator for initial Uniswap oracle deployment.
            // Will be burned after oracle deployment
            _mint(msg.sender, 1 * 10**18);
        }
        /**
         * @notice Operator mints basis cash to a recipient
         * @param recipient_ The address of recipient
         * @param amount_ The amount of basis cash to mint to
         */
        function mint(address recipient_, uint256 amount_)
            public
            onlyOperator
            returns (bool)
        {
            uint256 balanceBefore = balanceOf(recipient_);
            _mint(recipient_, amount_);
            uint256 balanceAfter = balanceOf(recipient_);
            return balanceAfter >= balanceBefore;
        }
        function burn(uint256 amount) public override onlyOperator {
            super.burn(amount);
        }
        function burnFrom(address account, uint256 amount)
            public
            override
            onlyOperator
        {
            super.burnFrom(account, amount);
        }
    }
    pragma solidity ^0.6.0;
    import '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol';
    import './owner/Operator.sol';
    contract Cash is ERC20Burnable, Operator {
        /**
         * @notice Constructs the Basis Cash ERC-20 contract.
         */
        constructor() public ERC20('BAC', 'BAC') {
            // Mints 1 Basis Cash to contract creator for initial Uniswap oracle deployment.
            // Will be burned after oracle deployment
            _mint(msg.sender, 1 * 10**18);
        }
        //    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        //        super._beforeTokenTransfer(from, to, amount);
        //        require(
        //            to != operator(),
        //            "basis.cash: operator as a recipient is not allowed"
        //        );
        //    }
        /**
         * @notice Operator mints basis cash to a recipient
         * @param recipient_ The address of recipient
         * @param amount_ The amount of basis cash to mint to
         * @return whether the process has been done
         */
        function mint(address recipient_, uint256 amount_)
            public
            onlyOperator
            returns (bool)
        {
            uint256 balanceBefore = balanceOf(recipient_);
            _mint(recipient_, amount_);
            uint256 balanceAfter = balanceOf(recipient_);
            return balanceAfter > balanceBefore;
        }
        function burn(uint256 amount) public override onlyOperator {
            super.burn(amount);
        }
        function burnFrom(address account, uint256 amount)
            public
            override
            onlyOperator
        {
            super.burnFrom(account, amount);
        }
    }
    pragma solidity ^0.6.0;
    import './owner/Operator.sol';
    import '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol';
    contract Bond is ERC20Burnable, Ownable, Operator {
        /**
         * @notice Constructs the Basis Bond ERC-20 contract.
         */
        constructor() public ERC20('BAB', 'BAB') {}
        /**
         * @notice Operator mints basis bonds to a recipient
         * @param recipient_ The address of recipient
         * @param amount_ The amount of basis bonds to mint to
         * @return whether the process has been done
         */
        function mint(address recipient_, uint256 amount_)
            public
            onlyOperator
            returns (bool)
        {
            uint256 balanceBefore = balanceOf(recipient_);
            _mint(recipient_, amount_);
            uint256 balanceAfter = balanceOf(recipient_);
            return balanceAfter > balanceBefore;
        }
        function burn(uint256 amount) public override onlyOperator {
            super.burn(amount);
        }
        function burnFrom(address account, uint256 amount)
            public
            override
            onlyOperator
        {
            super.burnFrom(account, amount);
        }
    }
    pragma solidity ^0.6.0;
    interface IUniswapV2Callee {
        function uniswapV2Call(
            address sender,
            uint256 amount0,
            uint256 amount1,
            bytes calldata data
        ) external;
    }
    pragma solidity ^0.6.0;
    interface IUniswapV2ERC20 {
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
        event Transfer(address indexed from, address indexed to, uint256 value);
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint256);
        function balanceOf(address owner) external view returns (uint256);
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
        function approve(address spender, uint256 value) external returns (bool);
        function transfer(address to, uint256 value) external returns (bool);
        function transferFrom(
            address from,
            address to,
            uint256 value
        ) external returns (bool);
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint256);
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.4.22 <0.8.0;
    contract Migrations {
        address public owner;
        uint256 public last_completed_migration;
        constructor() public {
            owner = msg.sender;
        }
        modifier restricted() {
            if (msg.sender == owner) _;
        }
        function setCompleted(uint256 completed) public restricted {
            last_completed_migration = completed;
        }
    }
    

    File 2 of 2: ShareV2
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.7.0 <0.8.0;
    import {
        ERC20,
        ERC20Burnable
    } from '@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol';
    import {Operator} from '../access/Operator.sol';
    contract ShareV2 is ERC20Burnable, Operator {
        /**
         * @notice Constructs the Basis Cash ERC-20 contract.
         */
        constructor() ERC20('BASv2', 'BASv2') {}
        /**
         * @notice Operator mints basis cash to a recipient
         * @param recipient_ The address of recipient
         * @param amount_ The amount of basis cash to mint to
         */
        function mint(address recipient_, uint256 amount_)
            public
            onlyOperator
            returns (bool)
        {
            uint256 balanceBefore = balanceOf(recipient_);
            _mint(recipient_, amount_);
            uint256 balanceAfter = balanceOf(recipient_);
            return balanceAfter >= balanceBefore;
        }
        function burn(uint256 amount) public override onlyOperator {
            super.burn(amount);
        }
        function burnFrom(address account, uint256 amount)
            public
            override
            onlyOperator
        {
            super.burnFrom(account, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    import "../../utils/Context.sol";
    import "./ERC20.sol";
    /**
     * @dev Extension of {ERC20} that allows token holders to destroy both their own
     * tokens and those that they have an allowance for, in a way that can be
     * recognized off-chain (via event analysis).
     */
    abstract contract ERC20Burnable is Context, ERC20 {
        using SafeMath for uint256;
        /**
         * @dev Destroys `amount` tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 amount) public virtual {
            _burn(_msgSender(), amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, deducting from the caller's
         * allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `amount`.
         */
        function burnFrom(address account, uint256 amount) public virtual {
            uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
            _approve(account, _msgSender(), decreasedAllowance);
            _burn(account, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.7.0 <0.8.0;
    import {Context, Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
    abstract contract Operator is Context, Ownable {
        address private _operator;
        event OperatorTransferred(
            address indexed previousOperator,
            address indexed newOperator
        );
        constructor() {
            _operator = _msgSender();
            emit OperatorTransferred(address(0), _operator);
        }
        function operator() public view returns (address) {
            return _operator;
        }
        modifier onlyOperator() {
            require(
                _operator == _msgSender(),
                'operator: caller is not the operator'
            );
            _;
        }
        function isOperator() public view returns (bool) {
            return _msgSender() == _operator;
        }
        function transferOperator(address newOperator_) public onlyOwner {
            _transferOperator(newOperator_);
        }
        function _transferOperator(address newOperator_) internal {
            require(
                newOperator_ != address(0),
                'operator: zero address given for new operator'
            );
            emit OperatorTransferred(address(0), newOperator_);
            _operator = newOperator_;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with GSN meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address payable) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    import "../../utils/Context.sol";
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20 {
        using SafeMath for uint256;
        mapping (address => uint256) private _balances;
        mapping (address => mapping (address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        uint8 private _decimals;
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name_, string memory symbol_) public {
            _name = name_;
            _symbol = symbol_;
            _decimals = 18;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual returns (uint8) {
            return _decimals;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
            return true;
        }
        /**
         * @dev Moves tokens `amount` from `sender` to `recipient`.
         *
         * This is internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(address sender, address recipient, uint256 amount) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
            _balances[recipient] = _balances[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Transfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
            _totalSupply = _totalSupply.sub(amount);
            emit Transfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Sets {decimals} to a value other than the default one of 18.
         *
         * WARNING: This function should only be called from the constructor. Most
         * applications that interact with token contracts will not expect
         * {decimals} to ever change, and may work incorrectly if it does.
         */
        function _setupDecimals(uint8 decimals_) internal virtual {
            _decimals = decimals_;
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "SafeMath: subtraction overflow");
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) return 0;
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: division by zero");
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: modulo by zero");
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            return a - b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryDiv}.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () internal {
            address msgSender = _msgSender();
            _owner = msgSender;
            emit OwnershipTransferred(address(0), msgSender);
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }