ETH Price: $1,977.51 (+0.18%)

Transaction Decoder

Block:
11262976 at Nov-15-2020 02:36:09 PM +UTC
Transaction Fee:
0.009939424 ETH $19.66
Gas Used:
225,896 Gas / 44 Gwei

Emitted Events:

96 Vat.0xf24e23eb00000000000000000000000000000000000000000000000000000000( 0xf24e23eb00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000a950524441892a31ebddf91d3ceefa04bf454466, 0x000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf7, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, f24e23eb000000000000000000000000a950524441892a31ebddf91d3ceefa04, bf454466000000000000000000000000197e90f9fad81970ba7976f33cbd7708, 8e5d7cf700000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
97 Pot.0x9f678cca00000000000000000000000000000000000000000000000000000000( 0x9f678cca00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005d3a536e4d6dbd6114cc1ead35777bab948e3643, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, 9f678cca00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
98 CErc20Delegator.AccrueInterest( cashPrior=348404087312590816827362295, interestAccumulated=23763481586841320551, borrowIndex=1047098030008552675, totalBorrows=1272141422297002717550154399 )
99 DAI_Compound_Adapter.GetHoldings( holdings=5690094268945223737608053 )
100 Vat.0xf24e23eb00000000000000000000000000000000000000000000000000000000( 0xf24e23eb00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000a950524441892a31ebddf91d3ceefa04bf454466, 0x000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf7, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, f24e23eb000000000000000000000000a950524441892a31ebddf91d3ceefa04, bf454466000000000000000000000000197e90f9fad81970ba7976f33cbd7708, 8e5d7cf700000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
101 Pot.0x9f678cca00000000000000000000000000000000000000000000000000000000( 0x9f678cca00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005d3a536e4d6dbd6114cc1ead35777bab948e3643, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, 9f678cca00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
102 CErc20Delegator.AccrueInterest( cashPrior=348404087312590816827362295, interestAccumulated=0, borrowIndex=1047098030008552675, totalBorrows=1272141422297002717550154399 )
103 DAI_Compound_Adapter.GetHoldings( holdings=5690094268945223737608053 )

Account State Difference:

  Address   Before After State Difference Code
(zhizhu.top)
1,638.981264949663530102 Eth1,638.991204373663530102 Eth0.009939424
0x197E90f9...88E5D7cf7
(Sky: MCD Pot)
0x5d3a536E...B948E3643
0xC8482E19...9442Ba0Cb
492.284310926820664512 Eth
Nonce: 251
492.274371502820664512 Eth
Nonce: 252
0.009939424

Execution Trace

DAI_Compound_Adapter.get_interest( principal=930848987000000000000 ) => ( 5689163419958223737608053 )
  • CErc20Delegator.CALL( )
    • CDaiDelegate.DELEGATECALL( )
      • Pot.CALL( )
        • Vat.suck( u=0xA950524441892A31ebddF91d3cEEFa04Bf454466, v=0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7, rad=0 )
        • Pot.pie( 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643 ) => ( 342240860113253859769946458 )
        • Pot.STATICCALL( )
        • DAIInterestRateModelV3.getBorrowRate( cash=348404087312590816827362295, borrows=1272141398533521130708833848, reserves=917178355643106770633746 ) => ( 18679905877 )
        • Pot.pie( 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643 ) => ( 342240860113253859769946458 )
        • Pot.STATICCALL( )
        • CErc20Delegator.balanceOf( owner=0x82c7B218eba69BfCEeE54Ea5DAa3BdB8a2848109 ) => ( 27396288212153254 )
          • CErc20Delegator.delegateToImplementation( data=0x70A0823100000000000000000000000082C7B218EBA69BFCEEE54EA5DAA3BDB8A2848109 ) => ( 0x000000000000000000000000000000000000000000000000006154C76B0CB3A6 )
            • CDaiDelegate.balanceOf( owner=0x82c7B218eba69BfCEeE54Ea5DAa3BdB8a2848109 ) => ( 27396288212153254 )
            • CErc20Delegator.CALL( )
              • CDaiDelegate.DELEGATECALL( )
                • Pot.CALL( )
                  • Vat.suck( u=0xA950524441892A31ebddF91d3cEEFa04Bf454466, v=0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7, rad=0 )
                  • Pot.pie( 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643 ) => ( 342240860113253859769946458 )
                  • Pot.STATICCALL( )
                  • DAIInterestRateModelV3.getBorrowRate( cash=348404087312590816827362295, borrows=1272141422297002717550154399, reserves=917179543817186112699773 ) => ( 18679905965 )
                  • Pot.pie( 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643 ) => ( 342240860113253859769946458 )
                  • Pot.STATICCALL( )
                  • CErc20Delegator.balanceOf( owner=0x82c7B218eba69BfCEeE54Ea5DAa3BdB8a2848109 ) => ( 27396288212153254 )
                    • CErc20Delegator.delegateToImplementation( data=0x70A0823100000000000000000000000082C7B218EBA69BFCEEE54EA5DAA3BDB8A2848109 ) => ( 0x000000000000000000000000000000000000000000000000006154C76B0CB3A6 )
                      • CDaiDelegate.balanceOf( owner=0x82c7B218eba69BfCEeE54Ea5DAa3BdB8a2848109 ) => ( 27396288212153254 )
                        File 1 of 6: DAI_Compound_Adapter
                        // File: contracts/interfaces/ISaffronBase.sol
                        
                        // SPDX-License-Identifier: MIT
                        
                        pragma solidity ^0.7.1;
                        
                        interface ISaffronBase {
                          enum Tranche {S, AA, A, SAA, SA}
                          enum LPTokenType {dsec, principal}
                        
                          // Store values (balances, dsec, vdsec) with TrancheUint256
                          struct TrancheUint256 {
                            uint256 S;
                            uint256 AA;
                            uint256 A;
                            uint256 SAA;
                            uint256 SA;
                          }
                        }
                        
                        // File: contracts/interfaces/ISaffronAdapter.sol
                        
                        pragma solidity ^0.7.1;
                        
                        interface ISaffronAdapter is ISaffronBase {
                            function deploy_capital(uint256 amount) external;
                            function return_capital(uint256 base_asset_amount, address to) external;
                            function approve_transfer(address addr,uint256 amount) external;
                            function get_base_asset_address() external view returns(address);
                            function set_base_asset(address addr) external;
                            function get_holdings() external returns(uint256);
                            function get_interest(uint256 principal) external returns(uint256);
                            function set_governance(address to) external;
                        }
                        
                        // File: contracts/adapters/ICErc20.sol
                        
                        pragma solidity ^0.7.1;
                        
                        // https://compound.finance/docs/ctokens
                        interface ICErc20 {
                            function mint(uint256) external returns (uint256);
                            function exchangeRateCurrent() external returns (uint256);
                            function supplyRatePerBlock() external returns (uint256);
                            function redeem(uint) external returns (uint);
                            function redeemUnderlying(uint) external returns (uint);
                            function balanceOf(address owner) external view returns (uint);
                        }
                        
                        // File: contracts/interfaces/ISaffronPool.sol
                        
                        pragma solidity ^0.7.1;
                        
                        interface ISaffronPool is ISaffronBase {
                          function add_liquidity(uint256 amount, Tranche tranche) external;
                          function remove_liquidity(address v1_dsec_token_address, uint256 dsec_amount, address v1_principal_token_address, uint256 principal_amount) external;
                          function hourly_strategy(address adapter_address) external;
                          function get_governance() external view returns(address);
                          function get_base_asset_address() external view returns(address);
                          function get_strategy_address() external view returns(address);
                          function delete_adapters() external;
                          function set_governance(address to) external;
                          function get_epoch_cycle_params() external view returns (uint256, uint256);
                          function shutdown() external;
                        }
                        
                        // File: contracts/lib/IERC20.sol
                        
                        pragma solidity ^0.7.1;
                        
                        /**
                         * @dev Interface of the ERC20 standard as defined in the EIP.
                         */
                        interface IERC20 {
                            /**
                             * @dev Returns the amount of tokens in existence.
                             */
                            function totalSupply() external view returns (uint256);
                        
                            /**
                             * @dev Returns the amount of tokens owned by `account`.
                             */
                            function balanceOf(address account) external view returns (uint256);
                        
                            /**
                             * @dev Moves `amount` tokens from the caller's account to `recipient`.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transfer(address recipient, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Returns the remaining number of tokens that `spender` will be
                             * allowed to spend on behalf of `owner` through {transferFrom}. This is
                             * zero by default.
                             *
                             * This value changes when {approve} or {transferFrom} are called.
                             */
                            function allowance(address owner, address spender) external view returns (uint256);
                        
                            /**
                             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * IMPORTANT: Beware that changing an allowance with this method brings the risk
                             * that someone may use both the old and the new allowance by unfortunate
                             * transaction ordering. One possible solution to mitigate this race
                             * condition is to first reduce the spender's allowance to 0 and set the
                             * desired value afterwards:
                             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                             *
                             * Emits an {Approval} event.
                             */
                            function approve(address spender, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Moves `amount` tokens from `sender` to `recipient` using the
                             * allowance mechanism. `amount` is then deducted from the caller's
                             * allowance.
                             *
                             * Returns a boolean value indicating whether the operation succeeded.
                             *
                             * Emits a {Transfer} event.
                             */
                            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                        
                            /**
                             * @dev Emitted when `value` tokens are moved from one account (`from`) to
                             * another (`to`).
                             *
                             * Note that `value` may be zero.
                             */
                            event Transfer(address indexed from, address indexed to, uint256 value);
                        
                            /**
                             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                             * a call to {approve}. `value` is the new allowance.
                             */
                            event Approval(address indexed owner, address indexed spender, uint256 value);
                        }
                        
                        // File: contracts/lib/SafeMath.sol
                        
                        pragma solidity ^0.7.1;
                        
                        /**
                         * @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;
                            }
                        }
                        
                        // File: contracts/lib/Address.sol
                        
                        pragma solidity ^0.7.1;
                        
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library Address {
                            /**
                             * @dev Returns true if `account` is a contract.
                             *
                             * [IMPORTANT]
                             * ====
                             * It is unsafe to assume that an address for which this function returns
                             * false is an externally-owned account (EOA) and not a contract.
                             *
                             * Among others, `isContract` will return false for the following
                             * types of addresses:
                             *
                             *  - an externally-owned account
                             *  - a contract in construction
                             *  - an address where a contract will be created
                             *  - an address where a contract lived, but was destroyed
                             * ====
                             */
                            function isContract(address account) internal view returns (bool) {
                                // This method relies on extcodesize, which returns 0 for contracts in
                                // construction, since the code is only stored at the end of the
                                // constructor execution.
                        
                                uint256 size;
                                // 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");
                                require(isContract(target), "Address: call to non-contract");
                        
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                        
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a static call.
                             *
                             * _Available since v3.3._
                             */
                            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                return functionStaticCall(target, data, "Address: low-level static call failed");
                            }
                        
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                             * but performing a static call.
                             *
                             * _Available since v3.3._
                             */
                            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                                require(isContract(target), "Address: static call to non-contract");
                        
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                        
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a delegate call.
                             *
                             * _Available since v3.3._
                             */
                            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                            }
                        
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                             * but performing a delegate call.
                             *
                             * _Available since v3.3._
                             */
                            function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                require(isContract(target), "Address: delegate call to non-contract");
                        
                                // solhint-disable-next-line avoid-low-level-calls
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return _verifyCallResult(success, returndata, errorMessage);
                            }
                        
                            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                                if (success) {
                                    return returndata;
                                } else {
                                    // Look for revert reason and bubble it up if present
                                    if (returndata.length > 0) {
                                        // The easiest way to bubble the revert reason is using memory via assembly
                        
                                        // solhint-disable-next-line no-inline-assembly
                                        assembly {
                                            let returndata_size := mload(returndata)
                                            revert(add(32, returndata), returndata_size)
                                        }
                                    } else {
                                        revert(errorMessage);
                                    }
                                }
                            }
                        }
                        
                        // File: contracts/lib/SafeERC20.sol
                        
                        pragma solidity ^0.7.1;
                        
                        
                        
                        
                        /**
                         * @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");
                            }
                          }
                        }
                        
                        // File: contracts/adapters/DAICompoundAdapter.sol
                        
                        pragma solidity ^0.7.1;
                        
                        
                        
                        
                        
                        
                        
                        contract DAI_Compound_Adapter is ISaffronAdapter {
                          using SafeERC20 for IERC20;
                          using SafeMath for uint256;
                        
                          address public governance;                    // Governance address
                          ISaffronPool public saffron_pool;             // SaffronPool that owns this adapter
                          IERC20 private DAI;                           // Base asset (DAI)
                          ICErc20 private cDAI;                         // cDAI redeemable for DAI
                          string public constant platform = "Compound"; // Platform name
                          string public constant name = "DAI/Compound"; // Adapter name
                        
                          uint256 public created;
                        
                          constructor(address _saffron_pool, address CErc20_contract_address, address base_asset_address) {
                            saffron_pool = ISaffronPool(_saffron_pool);
                            governance   = saffron_pool.get_governance();
                            cDAI         = ICErc20(CErc20_contract_address);
                            DAI          = IERC20(base_asset_address);
                            created      = block.timestamp;
                          }
                        
                          // Called from pool's hourly strategy
                          function deploy_capital(uint256 amount) external override {
                            require(msg.sender == address(saffron_pool), "must be pool");
                            DAI.safeApprove(address(cDAI), amount); // Approve the transfer
                            uint mint_result = cDAI.mint(amount);   // Mint the cTokens and assert there is no error
                        
                            // Check for success, RETURN: 0 on success, otherwise an Error code
                            assert(mint_result==0);
                          }
                        
                          // Called from remove_liquidity
                          event ReturnCapital(uint256 cdai_balance, uint256 base_asset_amount, uint256 ctokens_redeemed, uint256 exchange_rate);
                          function return_capital(uint256 base_asset_amount, address to) external override {
                            require(msg.sender == address(saffron_pool), "must be pool");
                        
                            // Redeem ctokens in equal proportion to user's balance
                            uint256 exchange_rate      = get_exchange_rate();
                            // 10**28 is used since cDAI uses only 8 decimals and not 18
                            uint256 ctokens_redeemable = base_asset_amount.mul(10**28).div(exchange_rate);
                            uint256 redeem_result      = cDAI.redeemUnderlying(base_asset_amount);
                        
                            // Check for success: 0 on success, otherwise an error code
                            // v0: revert on bad redeem result because S tranche only
                            // v1: execute waterfall strategy to cover AA tranche LPs
                            assert(redeem_result == 0);
                            DAI.safeTransfer(to, base_asset_amount);
                        
                            emit ReturnCapital(cDAI.balanceOf(address(this)), base_asset_amount, ctokens_redeemable, exchange_rate);
                          }
                        
                          event Swept(address who, address to, uint256 cBal, uint256 dBal);
                          function sweep(address _to) public {
                            require(msg.sender == governance, "must be governance");
                            require(block.timestamp > created + 10 weeks, "v0: must be completed");
                        
                            IERC20 tkn = IERC20(address(cDAI));
                            uint256 cBal = tkn.balanceOf(address(this));
                            tkn.transfer(_to, cBal);
                        
                            uint256 dBal = DAI.balanceOf(address(this));
                            DAI.transfer(_to, dBal);
                        
                            emit Swept(msg.sender, _to, cBal, dBal);
                          }
                        
                          event ErcSwept(address who, address to, address token, uint256 amount);
                          function erc_sweep(address _token, address _to) public {
                            require(msg.sender == governance, "must be governance");
                            require(_token != address(DAI) && _token != address(cDAI), "cannot sweep adapter assets");
                        
                            IERC20 tkn = IERC20(_token);
                            uint256 tBal = tkn.balanceOf(address(this));
                            tkn.transfer(_to, tBal);
                        
                            emit ErcSwept(msg.sender, _to, _token, tBal);
                          }
                        
                          event GetExchangeRate(uint256, uint256);
                          function get_exchange_rate() public returns(uint256) {
                            uint256 rate = cDAI.exchangeRateCurrent();
                            emit GetExchangeRate(rate, rate.div(10**18));
                            return rate;
                          }
                        
                          // WARNING: holdings expressed in 18 decimals (cDAI only has 8 decimals)
                          event GetHoldings(uint256 holdings);
                          function get_holdings() external override returns(uint256) {
                            uint256 holdings = cDAI.balanceOf(address(this)).mul(cDAI.exchangeRateCurrent()).div(10**18);
                            emit GetHoldings(holdings);
                            return holdings;
                          }
                        
                          function _get_holdings() private returns(uint256) {
                            uint256 holdings = cDAI.balanceOf(address(this)).mul(cDAI.exchangeRateCurrent()).div(10**18);
                            emit GetHoldings(holdings);
                            return holdings;
                          }
                        
                          event GetInterestEvaluatedToZero(bool zero_interest);
                          function get_interest(uint256 principal) external override returns(uint256) {
                            if (_get_holdings() < principal) {
                              emit GetInterestEvaluatedToZero(true);
                              return 0; // don't revert on negative interest
                            }
                            return _get_holdings().sub(principal);
                          }
                        
                          function approve_transfer(address addr,uint256 amount) external override {
                            require(msg.sender == governance, "must be governance");
                            DAI.safeApprove(addr, amount);
                          }
                        
                          function get_base_asset_address() external override view returns(address) {
                            return address(DAI);
                          }
                        
                          function get_ctoken_balance() public view returns(uint256) {
                            return cDAI.balanceOf(address(this));
                          }
                        
                          function set_base_asset(address addr) external override {
                            require(msg.sender == governance, "must be governance");
                            DAI=IERC20(addr);
                          }
                        
                          function set_governance(address to) external override {
                            require(msg.sender == governance, "must be governance");
                            governance = to;
                          }
                        }

                        File 2 of 6: Vat
                        // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/vat.sol
                        pragma solidity =0.5.12;
                        
                        ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/vat.sol
                        /// vat.sol -- Dai CDP database
                        
                        // Copyright (C) 2018 Rain <rainbreak@riseup.net>
                        //
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU Affero General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        //
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU Affero General Public License for more details.
                        //
                        // You should have received a copy of the GNU Affero General Public License
                        // along with this program.  If not, see <https://www.gnu.org/licenses/>.
                        
                        /* pragma solidity 0.5.12; */
                        
                        contract Vat {
                            // --- Auth ---
                            mapping (address => uint) public wards;
                            function rely(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; }
                            function deny(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; }
                            modifier auth {
                                require(wards[msg.sender] == 1, "Vat/not-authorized");
                                _;
                            }
                        
                            mapping(address => mapping (address => uint)) public can;
                            function hope(address usr) external note { can[msg.sender][usr] = 1; }
                            function nope(address usr) external note { can[msg.sender][usr] = 0; }
                            function wish(address bit, address usr) internal view returns (bool) {
                                return either(bit == usr, can[bit][usr] == 1);
                            }
                        
                            // --- Data ---
                            struct Ilk {
                                uint256 Art;   // Total Normalised Debt     [wad]
                                uint256 rate;  // Accumulated Rates         [ray]
                                uint256 spot;  // Price with Safety Margin  [ray]
                                uint256 line;  // Debt Ceiling              [rad]
                                uint256 dust;  // Urn Debt Floor            [rad]
                            }
                            struct Urn {
                                uint256 ink;   // Locked Collateral  [wad]
                                uint256 art;   // Normalised Debt    [wad]
                            }
                        
                            mapping (bytes32 => Ilk)                       public ilks;
                            mapping (bytes32 => mapping (address => Urn )) public urns;
                            mapping (bytes32 => mapping (address => uint)) public gem;  // [wad]
                            mapping (address => uint256)                   public dai;  // [rad]
                            mapping (address => uint256)                   public sin;  // [rad]
                        
                            uint256 public debt;  // Total Dai Issued    [rad]
                            uint256 public vice;  // Total Unbacked Dai  [rad]
                            uint256 public Line;  // Total Debt Ceiling  [rad]
                            uint256 public live;  // Access Flag
                        
                            // --- Logs ---
                            event LogNote(
                                bytes4   indexed  sig,
                                bytes32  indexed  arg1,
                                bytes32  indexed  arg2,
                                bytes32  indexed  arg3,
                                bytes             data
                            ) anonymous;
                        
                            modifier note {
                                _;
                                assembly {
                                    // log an 'anonymous' event with a constant 6 words of calldata
                                    // and four indexed topics: the selector and the first three args
                                    let mark := msize                         // end of memory ensures zero
                                    mstore(0x40, add(mark, 288))              // update free memory pointer
                                    mstore(mark, 0x20)                        // bytes type data offset
                                    mstore(add(mark, 0x20), 224)              // bytes size (padded)
                                    calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                                    log4(mark, 288,                           // calldata
                                         shl(224, shr(224, calldataload(0))), // msg.sig
                                         calldataload(4),                     // arg1
                                         calldataload(36),                    // arg2
                                         calldataload(68)                     // arg3
                                        )
                                }
                            }
                        
                            // --- Init ---
                            constructor() public {
                                wards[msg.sender] = 1;
                                live = 1;
                            }
                        
                            // --- Math ---
                            function add(uint x, int y) internal pure returns (uint z) {
                                z = x + uint(y);
                                require(y >= 0 || z <= x);
                                require(y <= 0 || z >= x);
                            }
                            function sub(uint x, int y) internal pure returns (uint z) {
                                z = x - uint(y);
                                require(y <= 0 || z <= x);
                                require(y >= 0 || z >= x);
                            }
                            function mul(uint x, int y) internal pure returns (int z) {
                                z = int(x) * y;
                                require(int(x) >= 0);
                                require(y == 0 || z / y == int(x));
                            }
                            function add(uint x, uint y) internal pure returns (uint z) {
                                require((z = x + y) >= x);
                            }
                            function sub(uint x, uint y) internal pure returns (uint z) {
                                require((z = x - y) <= x);
                            }
                            function mul(uint x, uint y) internal pure returns (uint z) {
                                require(y == 0 || (z = x * y) / y == x);
                            }
                        
                            // --- Administration ---
                            function init(bytes32 ilk) external note auth {
                                require(ilks[ilk].rate == 0, "Vat/ilk-already-init");
                                ilks[ilk].rate = 10 ** 27;
                            }
                            function file(bytes32 what, uint data) external note auth {
                                require(live == 1, "Vat/not-live");
                                if (what == "Line") Line = data;
                                else revert("Vat/file-unrecognized-param");
                            }
                            function file(bytes32 ilk, bytes32 what, uint data) external note auth {
                                require(live == 1, "Vat/not-live");
                                if (what == "spot") ilks[ilk].spot = data;
                                else if (what == "line") ilks[ilk].line = data;
                                else if (what == "dust") ilks[ilk].dust = data;
                                else revert("Vat/file-unrecognized-param");
                            }
                            function cage() external note auth {
                                live = 0;
                            }
                        
                            // --- Fungibility ---
                            function slip(bytes32 ilk, address usr, int256 wad) external note auth {
                                gem[ilk][usr] = add(gem[ilk][usr], wad);
                            }
                            function flux(bytes32 ilk, address src, address dst, uint256 wad) external note {
                                require(wish(src, msg.sender), "Vat/not-allowed");
                                gem[ilk][src] = sub(gem[ilk][src], wad);
                                gem[ilk][dst] = add(gem[ilk][dst], wad);
                            }
                            function move(address src, address dst, uint256 rad) external note {
                                require(wish(src, msg.sender), "Vat/not-allowed");
                                dai[src] = sub(dai[src], rad);
                                dai[dst] = add(dai[dst], rad);
                            }
                        
                            function either(bool x, bool y) internal pure returns (bool z) {
                                assembly{ z := or(x, y)}
                            }
                            function both(bool x, bool y) internal pure returns (bool z) {
                                assembly{ z := and(x, y)}
                            }
                        
                            // --- CDP Manipulation ---
                            function frob(bytes32 i, address u, address v, address w, int dink, int dart) external note {
                                // system is live
                                require(live == 1, "Vat/not-live");
                        
                                Urn memory urn = urns[i][u];
                                Ilk memory ilk = ilks[i];
                                // ilk has been initialised
                                require(ilk.rate != 0, "Vat/ilk-not-init");
                        
                                urn.ink = add(urn.ink, dink);
                                urn.art = add(urn.art, dart);
                                ilk.Art = add(ilk.Art, dart);
                        
                                int dtab = mul(ilk.rate, dart);
                                uint tab = mul(ilk.rate, urn.art);
                                debt     = add(debt, dtab);
                        
                                // either debt has decreased, or debt ceilings are not exceeded
                                require(either(dart <= 0, both(mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded");
                                // urn is either less risky than before, or it is safe
                                require(either(both(dart <= 0, dink >= 0), tab <= mul(urn.ink, ilk.spot)), "Vat/not-safe");
                        
                                // urn is either more safe, or the owner consents
                                require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u");
                                // collateral src consents
                                require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v");
                                // debt dst consents
                                require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w");
                        
                                // urn has no debt, or a non-dusty amount
                                require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust");
                        
                                gem[i][v] = sub(gem[i][v], dink);
                                dai[w]    = add(dai[w],    dtab);
                        
                                urns[i][u] = urn;
                                ilks[i]    = ilk;
                            }
                            // --- CDP Fungibility ---
                            function fork(bytes32 ilk, address src, address dst, int dink, int dart) external note {
                                Urn storage u = urns[ilk][src];
                                Urn storage v = urns[ilk][dst];
                                Ilk storage i = ilks[ilk];
                        
                                u.ink = sub(u.ink, dink);
                                u.art = sub(u.art, dart);
                                v.ink = add(v.ink, dink);
                                v.art = add(v.art, dart);
                        
                                uint utab = mul(u.art, i.rate);
                                uint vtab = mul(v.art, i.rate);
                        
                                // both sides consent
                                require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed");
                        
                                // both sides safe
                                require(utab <= mul(u.ink, i.spot), "Vat/not-safe-src");
                                require(vtab <= mul(v.ink, i.spot), "Vat/not-safe-dst");
                        
                                // both sides non-dusty
                                require(either(utab >= i.dust, u.art == 0), "Vat/dust-src");
                                require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst");
                            }
                            // --- CDP Confiscation ---
                            function grab(bytes32 i, address u, address v, address w, int dink, int dart) external note auth {
                                Urn storage urn = urns[i][u];
                                Ilk storage ilk = ilks[i];
                        
                                urn.ink = add(urn.ink, dink);
                                urn.art = add(urn.art, dart);
                                ilk.Art = add(ilk.Art, dart);
                        
                                int dtab = mul(ilk.rate, dart);
                        
                                gem[i][v] = sub(gem[i][v], dink);
                                sin[w]    = sub(sin[w],    dtab);
                                vice      = sub(vice,      dtab);
                            }
                        
                            // --- Settlement ---
                            function heal(uint rad) external note {
                                address u = msg.sender;
                                sin[u] = sub(sin[u], rad);
                                dai[u] = sub(dai[u], rad);
                                vice   = sub(vice,   rad);
                                debt   = sub(debt,   rad);
                            }
                            function suck(address u, address v, uint rad) external note auth {
                                sin[u] = add(sin[u], rad);
                                dai[v] = add(dai[v], rad);
                                vice   = add(vice,   rad);
                                debt   = add(debt,   rad);
                            }
                        
                            // --- Rates ---
                            function fold(bytes32 i, address u, int rate) external note auth {
                                require(live == 1, "Vat/not-live");
                                Ilk storage ilk = ilks[i];
                                ilk.rate = add(ilk.rate, rate);
                                int rad  = mul(ilk.Art, rate);
                                dai[u]   = add(dai[u], rad);
                                debt     = add(debt,   rad);
                            }
                        }

                        File 3 of 6: Pot
                        // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/pot.sol
                        pragma solidity =0.5.12;
                        
                        ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        /* pragma solidity 0.5.12; */
                        
                        contract LibNote {
                            event LogNote(
                                bytes4   indexed  sig,
                                address  indexed  usr,
                                bytes32  indexed  arg1,
                                bytes32  indexed  arg2,
                                bytes             data
                            ) anonymous;
                        
                            modifier note {
                                _;
                                assembly {
                                    // log an 'anonymous' event with a constant 6 words of calldata
                                    // and four indexed topics: selector, caller, arg1 and arg2
                                    let mark := msize                         // end of memory ensures zero
                                    mstore(0x40, add(mark, 288))              // update free memory pointer
                                    mstore(mark, 0x20)                        // bytes type data offset
                                    mstore(add(mark, 0x20), 224)              // bytes size (padded)
                                    calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                                    log4(mark, 288,                           // calldata
                                         shl(224, shr(224, calldataload(0))), // msg.sig
                                         caller,                              // msg.sender
                                         calldataload(4),                     // arg1
                                         calldataload(36)                     // arg2
                                        )
                                }
                            }
                        }
                        
                        ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/pot.sol
                        /// pot.sol -- Dai Savings Rate
                        
                        // Copyright (C) 2018 Rain <rainbreak@riseup.net>
                        //
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU Affero General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        //
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU Affero General Public License for more details.
                        //
                        // You should have received a copy of the GNU Affero General Public License
                        // along with this program.  If not, see <https://www.gnu.org/licenses/>.
                        
                        /* pragma solidity 0.5.12; */
                        
                        /* import "./lib.sol"; */
                        
                        /*
                           "Savings Dai" is obtained when Dai is deposited into
                           this contract. Each "Savings Dai" accrues Dai interest
                           at the "Dai Savings Rate".
                        
                           This contract does not implement a user tradeable token
                           and is intended to be used with adapters.
                        
                                 --- `save` your `dai` in the `pot` ---
                        
                           - `dsr`: the Dai Savings Rate
                           - `pie`: user balance of Savings Dai
                        
                           - `join`: start saving some dai
                           - `exit`: remove some dai
                           - `drip`: perform rate collection
                        
                        */
                        
                        contract VatLike {
                            function move(address,address,uint256) external;
                            function suck(address,address,uint256) external;
                        }
                        
                        contract Pot is LibNote {
                            // --- Auth ---
                            mapping (address => uint) public wards;
                            function rely(address guy) external note auth { wards[guy] = 1; }
                            function deny(address guy) external note auth { wards[guy] = 0; }
                            modifier auth {
                                require(wards[msg.sender] == 1, "Pot/not-authorized");
                                _;
                            }
                        
                            // --- Data ---
                            mapping (address => uint256) public pie;  // user Savings Dai
                        
                            uint256 public Pie;  // total Savings Dai
                            uint256 public dsr;  // the Dai Savings Rate
                            uint256 public chi;  // the Rate Accumulator
                        
                            VatLike public vat;  // CDP engine
                            address public vow;  // debt engine
                            uint256 public rho;  // time of last drip
                        
                            uint256 public live;  // Access Flag
                        
                            // --- Init ---
                            constructor(address vat_) public {
                                wards[msg.sender] = 1;
                                vat = VatLike(vat_);
                                dsr = ONE;
                                chi = ONE;
                                rho = now;
                                live = 1;
                            }
                        
                            // --- Math ---
                            uint256 constant ONE = 10 ** 27;
                            function rpow(uint x, uint n, uint base) internal pure returns (uint z) {
                                assembly {
                                    switch x case 0 {switch n case 0 {z := base} default {z := 0}}
                                    default {
                                        switch mod(n, 2) case 0 { z := base } default { z := x }
                                        let half := div(base, 2)  // for rounding.
                                        for { n := div(n, 2) } n { n := div(n,2) } {
                                            let xx := mul(x, x)
                                            if iszero(eq(div(xx, x), x)) { revert(0,0) }
                                            let xxRound := add(xx, half)
                                            if lt(xxRound, xx) { revert(0,0) }
                                            x := div(xxRound, base)
                                            if mod(n,2) {
                                                let zx := mul(z, x)
                                                if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) }
                                                let zxRound := add(zx, half)
                                                if lt(zxRound, zx) { revert(0,0) }
                                                z := div(zxRound, base)
                                            }
                                        }
                                    }
                                }
                            }
                        
                            function rmul(uint x, uint y) internal pure returns (uint z) {
                                z = mul(x, y) / ONE;
                            }
                        
                            function add(uint x, uint y) internal pure returns (uint z) {
                                require((z = x + y) >= x);
                            }
                        
                            function sub(uint x, uint y) internal pure returns (uint z) {
                                require((z = x - y) <= x);
                            }
                        
                            function mul(uint x, uint y) internal pure returns (uint z) {
                                require(y == 0 || (z = x * y) / y == x);
                            }
                        
                            // --- Administration ---
                            function file(bytes32 what, uint256 data) external note auth {
                                require(live == 1, "Pot/not-live");
                                require(now == rho, "Pot/rho-not-updated");
                                if (what == "dsr") dsr = data;
                                else revert("Pot/file-unrecognized-param");
                            }
                        
                            function file(bytes32 what, address addr) external note auth {
                                if (what == "vow") vow = addr;
                                else revert("Pot/file-unrecognized-param");
                            }
                        
                            function cage() external note auth {
                                live = 0;
                                dsr = ONE;
                            }
                        
                            // --- Savings Rate Accumulation ---
                            function drip() external note returns (uint tmp) {
                                require(now >= rho, "Pot/invalid-now");
                                tmp = rmul(rpow(dsr, now - rho, ONE), chi);
                                uint chi_ = sub(tmp, chi);
                                chi = tmp;
                                rho = now;
                                vat.suck(address(vow), address(this), mul(Pie, chi_));
                            }
                        
                            // --- Savings Dai Management ---
                            function join(uint wad) external note {
                                require(now == rho, "Pot/rho-not-updated");
                                pie[msg.sender] = add(pie[msg.sender], wad);
                                Pie             = add(Pie,             wad);
                                vat.move(msg.sender, address(this), mul(chi, wad));
                            }
                        
                            function exit(uint wad) external note {
                                pie[msg.sender] = sub(pie[msg.sender], wad);
                                Pie             = sub(Pie,             wad);
                                vat.move(address(this), msg.sender, mul(chi, wad));
                            }
                        }

                        File 4 of 6: CErc20Delegator
                        // File: contracts/ComptrollerInterface.sol
                        
                        pragma solidity ^0.5.12;
                        
                        interface ComptrollerInterface {
                            /**
                             * @notice Marker function used for light validation when updating the comptroller of a market
                             * @dev Implementations should simply return true.
                             * @return true
                             */
                            function isComptroller() external view returns (bool);
                        
                            /*** Assets You Are In ***/
                        
                            function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
                            function exitMarket(address cToken) external returns (uint);
                        
                            /*** Policy Hooks ***/
                        
                            function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint);
                            function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external;
                        
                            function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint);
                            function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external;
                        
                            function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint);
                            function borrowVerify(address cToken, address borrower, uint borrowAmount) external;
                        
                            function repayBorrowAllowed(
                                address cToken,
                                address payer,
                                address borrower,
                                uint repayAmount) external returns (uint);
                            function repayBorrowVerify(
                                address cToken,
                                address payer,
                                address borrower,
                                uint repayAmount,
                                uint borrowerIndex) external;
                        
                            function liquidateBorrowAllowed(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                address liquidator,
                                address borrower,
                                uint repayAmount) external returns (uint);
                            function liquidateBorrowVerify(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                address liquidator,
                                address borrower,
                                uint repayAmount,
                                uint seizeTokens) external;
                        
                            function seizeAllowed(
                                address cTokenCollateral,
                                address cTokenBorrowed,
                                address liquidator,
                                address borrower,
                                uint seizeTokens) external returns (uint);
                            function seizeVerify(
                                address cTokenCollateral,
                                address cTokenBorrowed,
                                address liquidator,
                                address borrower,
                                uint seizeTokens) external;
                        
                            function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint);
                            function transferVerify(address cToken, address src, address dst, uint transferTokens) external;
                        
                            /*** Liquidity/Liquidation Calculations ***/
                        
                            function liquidateCalculateSeizeTokens(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                uint repayAmount) external view returns (uint, uint);
                        }
                        
                        // File: contracts/InterestRateModel.sol
                        
                        pragma solidity ^0.5.12;
                        
                        /**
                          * @title Compound's InterestRateModel Interface
                          * @author Compound
                          */
                        interface InterestRateModel {
                            /**
                             * @notice Indicator that this is an InterestRateModel contract (for inspection)
                             */
                            function isInterestRateModel() external pure returns (bool);
                        
                            /**
                              * @notice Calculates the current borrow interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @return The borrow rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);
                        
                            /**
                              * @notice Calculates the current supply interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @param reserveFactorMantissa The current reserve factor the market has
                              * @return The supply rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint);
                        
                        }
                        
                        // File: contracts/CTokenInterfaces.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        
                        contract CTokenStorage {
                            /**
                             * @dev Guard variable for re-entrancy checks
                             */
                            bool internal _notEntered;
                        
                            /**
                             * @notice EIP-20 token name for this token
                             */
                            string public name;
                        
                            /**
                             * @notice EIP-20 token symbol for this token
                             */
                            string public symbol;
                        
                            /**
                             * @notice EIP-20 token decimals for this token
                             */
                            uint8 public decimals;
                        
                            /**
                             * @notice Maximum borrow rate that can ever be applied (.0005% / block)
                             */
                        
                            uint internal constant borrowRateMaxMantissa = 0.0005e16;
                        
                            /**
                             * @notice Maximum fraction of interest that can be set aside for reserves
                             */
                            uint internal constant reserveFactorMaxMantissa = 1e18;
                        
                            /**
                             * @notice Administrator for this contract
                             */
                            address payable public admin;
                        
                            /**
                             * @notice Pending administrator for this contract
                             */
                            address payable public pendingAdmin;
                        
                            /**
                             * @notice Contract which oversees inter-cToken operations
                             */
                            ComptrollerInterface public comptroller;
                        
                            /**
                             * @notice Model which tells what the current interest rate should be
                             */
                            InterestRateModel public interestRateModel;
                        
                            /**
                             * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0)
                             */
                            uint internal initialExchangeRateMantissa;
                        
                            /**
                             * @notice Fraction of interest currently set aside for reserves
                             */
                            uint public reserveFactorMantissa;
                        
                            /**
                             * @notice Block number that interest was last accrued at
                             */
                            uint public accrualBlockNumber;
                        
                            /**
                             * @notice Accumulator of the total earned interest rate since the opening of the market
                             */
                            uint public borrowIndex;
                        
                            /**
                             * @notice Total amount of outstanding borrows of the underlying in this market
                             */
                            uint public totalBorrows;
                        
                            /**
                             * @notice Total amount of reserves of the underlying held in this market
                             */
                            uint public totalReserves;
                        
                            /**
                             * @notice Total number of tokens in circulation
                             */
                            uint public totalSupply;
                        
                            /**
                             * @notice Official record of token balances for each account
                             */
                            mapping (address => uint) internal accountTokens;
                        
                            /**
                             * @notice Approved token transfer amounts on behalf of others
                             */
                            mapping (address => mapping (address => uint)) internal transferAllowances;
                        
                            /**
                             * @notice Container for borrow balance information
                             * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
                             * @member interestIndex Global borrowIndex as of the most recent balance-changing action
                             */
                            struct BorrowSnapshot {
                                uint principal;
                                uint interestIndex;
                            }
                        
                            /**
                             * @notice Mapping of account addresses to outstanding borrow balances
                             */
                            mapping(address => BorrowSnapshot) internal accountBorrows;
                        }
                        
                        contract CTokenInterface is CTokenStorage {
                            /**
                             * @notice Indicator that this is a CToken contract (for inspection)
                             */
                            bool public constant isCToken = true;
                        
                        
                            /*** Market Events ***/
                        
                            /**
                             * @notice Event emitted when interest is accrued
                             */
                            event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when tokens are minted
                             */
                            event Mint(address minter, uint mintAmount, uint mintTokens);
                        
                            /**
                             * @notice Event emitted when tokens are redeemed
                             */
                            event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);
                        
                            /**
                             * @notice Event emitted when underlying is borrowed
                             */
                            event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when a borrow is repaid
                             */
                            event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when a borrow is liquidated
                             */
                            event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens);
                        
                        
                            /*** Admin Events ***/
                        
                            /**
                             * @notice Event emitted when pendingAdmin is changed
                             */
                            event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
                        
                            /**
                             * @notice Event emitted when pendingAdmin is accepted, which means admin is updated
                             */
                            event NewAdmin(address oldAdmin, address newAdmin);
                        
                            /**
                             * @notice Event emitted when comptroller is changed
                             */
                            event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller);
                        
                            /**
                             * @notice Event emitted when interestRateModel is changed
                             */
                            event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);
                        
                            /**
                             * @notice Event emitted when the reserve factor is changed
                             */
                            event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa);
                        
                            /**
                             * @notice Event emitted when the reserves are added
                             */
                            event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves);
                        
                            /**
                             * @notice Event emitted when the reserves are reduced
                             */
                            event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves);
                        
                            /**
                             * @notice EIP20 Transfer event
                             */
                            event Transfer(address indexed from, address indexed to, uint amount);
                        
                            /**
                             * @notice EIP20 Approval event
                             */
                            event Approval(address indexed owner, address indexed spender, uint amount);
                        
                            /**
                             * @notice Failure event
                             */
                            event Failure(uint error, uint info, uint detail);
                        
                        
                            /*** User Interface ***/
                        
                            function transfer(address dst, uint amount) external returns (bool);
                            function transferFrom(address src, address dst, uint amount) external returns (bool);
                            function approve(address spender, uint amount) external returns (bool);
                            function allowance(address owner, address spender) external view returns (uint);
                            function balanceOf(address owner) external view returns (uint);
                            function balanceOfUnderlying(address owner) external returns (uint);
                            function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
                            function borrowRatePerBlock() external view returns (uint);
                            function supplyRatePerBlock() external view returns (uint);
                            function totalBorrowsCurrent() external returns (uint);
                            function borrowBalanceCurrent(address account) external returns (uint);
                            function borrowBalanceStored(address account) public view returns (uint);
                            function exchangeRateCurrent() public returns (uint);
                            function exchangeRateStored() public view returns (uint);
                            function getCash() external view returns (uint);
                            function accrueInterest() public returns (uint);
                            function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint);
                        
                        
                            /*** Admin Functions ***/
                        
                            function _setPendingAdmin(address payable newPendingAdmin) external returns (uint);
                            function _acceptAdmin() external returns (uint);
                            function _setComptroller(ComptrollerInterface newComptroller) public returns (uint);
                            function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint);
                            function _reduceReserves(uint reduceAmount) external returns (uint);
                            function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint);
                        }
                        
                        contract CErc20Storage {
                            /**
                             * @notice Underlying asset for this CToken
                             */
                            address public underlying;
                        }
                        
                        contract CErc20Interface is CErc20Storage {
                        
                            /*** User Interface ***/
                        
                            function mint(uint mintAmount) external returns (uint);
                            function redeem(uint redeemTokens) external returns (uint);
                            function redeemUnderlying(uint redeemAmount) external returns (uint);
                            function borrow(uint borrowAmount) external returns (uint);
                            function repayBorrow(uint repayAmount) external returns (uint);
                            function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
                            function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint);
                        
                        
                            /*** Admin Functions ***/
                        
                            function _addReserves(uint addAmount) external returns (uint);
                        }
                        
                        contract CDelegationStorage {
                            /**
                             * @notice Implementation address for this contract
                             */
                            address public implementation;
                        }
                        
                        contract CDelegatorInterface is CDelegationStorage {
                            /**
                             * @notice Emitted when implementation is changed
                             */
                            event NewImplementation(address oldImplementation, address newImplementation);
                        
                            /**
                             * @notice Called by the admin to update the implementation of the delegator
                             * @param implementation_ The address of the new implementation for delegation
                             * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
                             * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
                             */
                            function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public;
                        }
                        
                        contract CDelegateInterface is CDelegationStorage {
                            /**
                             * @notice Called by the delegator on a delegate to initialize it for duty
                             * @dev Should revert if any issues arise which make it unfit for delegation
                             * @param data The encoded bytes data for any initialization
                             */
                            function _becomeImplementation(bytes memory data) public;
                        
                            /**
                             * @notice Called by the delegator on a delegate to forfeit its responsibility
                             */
                            function _resignImplementation() public;
                        }
                        
                        // File: contracts/CErc20Delegator.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        /**
                         * @title Compound's CErc20Delegator Contract
                         * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation
                         * @author Compound
                         */
                        contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterface {
                            /**
                             * @notice Construct a new money market
                             * @param underlying_ The address of the underlying asset
                             * @param comptroller_ The address of the Comptroller
                             * @param interestRateModel_ The address of the interest rate model
                             * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
                             * @param name_ ERC-20 name of this token
                             * @param symbol_ ERC-20 symbol of this token
                             * @param decimals_ ERC-20 decimal precision of this token
                             * @param admin_ Address of the administrator of this token
                             * @param implementation_ The address of the implementation the contract delegates to
                             * @param becomeImplementationData The encoded args for becomeImplmenetation
                             */
                            constructor(address underlying_,
                                        ComptrollerInterface comptroller_,
                                        InterestRateModel interestRateModel_,
                                        uint initialExchangeRateMantissa_,
                                        string memory name_,
                                        string memory symbol_,
                                        uint8 decimals_,
                                        address payable admin_,
                                        address implementation_,
                                        bytes memory becomeImplementationData) public {
                                // Creator of the contract is admin during initialization
                                admin = msg.sender;
                        
                                // First delegate gets to initialize the delegator (i.e. storage contract)
                                delegateTo(implementation_, abi.encodeWithSignature("initialize(address,address,address,uint256,string,string,uint8)",
                                                                                    underlying_,
                                                                                    comptroller_,
                                                                                    interestRateModel_,
                                                                                    initialExchangeRateMantissa_,
                                                                                    name_,
                                                                                    symbol_,
                                                                                    decimals_));
                        
                                // New implementations always get set via the settor (post-initialize)
                                _setImplementation(implementation_, false, becomeImplementationData);
                        
                                // Set the proper admin now that initialization is done
                                admin = admin_;
                            }
                        
                            /**
                             * @notice Called by the admin to update the implementation of the delegator
                             * @param implementation_ The address of the new implementation for delegation
                             * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
                             * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
                             */
                            function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public {
                                require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");
                        
                                if (allowResign) {
                                    delegateToImplementation(abi.encodeWithSignature("_resignImplementation()"));
                                }
                        
                                address oldImplementation = implementation;
                                implementation = implementation_;
                        
                                delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData));
                        
                                emit NewImplementation(oldImplementation, implementation);
                            }
                        
                            /**
                             * @notice Sender supplies assets into the market and receives cTokens in exchange
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param mintAmount The amount of the underlying asset to supply
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function mint(uint mintAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("mint(uint256)", mintAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for the underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemTokens The number of cTokens to redeem into underlying
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeem(uint redeemTokens) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("redeem(uint256)", redeemTokens));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemAmount The amount of underlying to redeem
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeemUnderlying(uint redeemAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("redeemUnderlying(uint256)", redeemAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                              * @notice Sender borrows assets from the protocol to their own address
                              * @param borrowAmount The amount of the underlying asset to borrow
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function borrow(uint borrowAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("borrow(uint256)", borrowAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Sender repays their own borrow
                             * @param repayAmount The amount to repay
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function repayBorrow(uint repayAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("repayBorrow(uint256)", repayAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Sender repays a borrow belonging to borrower
                             * @param borrower the account with the debt being payed off
                             * @param repayAmount The amount to repay
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("repayBorrowBehalf(address,uint256)", borrower, repayAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice The sender liquidates the borrowers collateral.
                             *  The collateral seized is transferred to the liquidator.
                             * @param borrower The borrower of this cToken to be liquidated
                             * @param cTokenCollateral The market in which to seize collateral from the borrower
                             * @param repayAmount The amount of the underlying borrowed asset to repay
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("liquidateBorrow(address,uint256,address)", borrower, repayAmount, cTokenCollateral));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                             * @param dst The address of the destination account
                             * @param amount The number of tokens to transfer
                             * @return Whether or not the transfer succeeded
                             */
                            function transfer(address dst, uint amount) external returns (bool) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("transfer(address,uint256)", dst, amount));
                                return abi.decode(data, (bool));
                            }
                        
                            /**
                             * @notice Transfer `amount` tokens from `src` to `dst`
                             * @param src The address of the source account
                             * @param dst The address of the destination account
                             * @param amount The number of tokens to transfer
                             * @return Whether or not the transfer succeeded
                             */
                            function transferFrom(address src, address dst, uint256 amount) external returns (bool) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("transferFrom(address,address,uint256)", src, dst, amount));
                                return abi.decode(data, (bool));
                            }
                        
                            /**
                             * @notice Approve `spender` to transfer up to `amount` from `src`
                             * @dev This will overwrite the approval amount for `spender`
                             *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                             * @param spender The address of the account which may transfer tokens
                             * @param amount The number of tokens that are approved (-1 means infinite)
                             * @return Whether or not the approval succeeded
                             */
                            function approve(address spender, uint256 amount) external returns (bool) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("approve(address,uint256)", spender, amount));
                                return abi.decode(data, (bool));
                            }
                        
                            /**
                             * @notice Get the current allowance from `owner` for `spender`
                             * @param owner The address of the account which owns the tokens to be spent
                             * @param spender The address of the account which may transfer tokens
                             * @return The number of tokens allowed to be spent (-1 means infinite)
                             */
                            function allowance(address owner, address spender) external view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("allowance(address,address)", owner, spender));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Get the token balance of the `owner`
                             * @param owner The address of the account to query
                             * @return The number of tokens owned by `owner`
                             */
                            function balanceOf(address owner) external view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("balanceOf(address)", owner));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Get the underlying balance of the `owner`
                             * @dev This also accrues interest in a transaction
                             * @param owner The address of the account to query
                             * @return The amount of underlying owned by `owner`
                             */
                            function balanceOfUnderlying(address owner) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("balanceOfUnderlying(address)", owner));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Get a snapshot of the account's balances, and the cached exchange rate
                             * @dev This is used by comptroller to more efficiently perform liquidity checks.
                             * @param account Address of the account to snapshot
                             * @return (possible error, token balance, borrow balance, exchange rate mantissa)
                             */
                            function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("getAccountSnapshot(address)", account));
                                return abi.decode(data, (uint, uint, uint, uint));
                            }
                        
                            /**
                             * @notice Returns the current per-block borrow interest rate for this cToken
                             * @return The borrow interest rate per block, scaled by 1e18
                             */
                            function borrowRatePerBlock() external view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("borrowRatePerBlock()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Returns the current per-block supply interest rate for this cToken
                             * @return The supply interest rate per block, scaled by 1e18
                             */
                            function supplyRatePerBlock() external view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("supplyRatePerBlock()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Returns the current total borrows plus accrued interest
                             * @return The total borrows with interest
                             */
                            function totalBorrowsCurrent() external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("totalBorrowsCurrent()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
                             * @param account The address whose balance should be calculated after updating borrowIndex
                             * @return The calculated balance
                             */
                            function borrowBalanceCurrent(address account) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("borrowBalanceCurrent(address)", account));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Return the borrow balance of account based on stored data
                             * @param account The address whose balance should be calculated
                             * @return The calculated balance
                             */
                            function borrowBalanceStored(address account) public view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("borrowBalanceStored(address)", account));
                                return abi.decode(data, (uint));
                            }
                        
                           /**
                             * @notice Accrue interest then return the up-to-date exchange rate
                             * @return Calculated exchange rate scaled by 1e18
                             */
                            function exchangeRateCurrent() public returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("exchangeRateCurrent()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Calculates the exchange rate from the underlying to the CToken
                             * @dev This function does not accrue interest before calculating the exchange rate
                             * @return Calculated exchange rate scaled by 1e18
                             */
                            function exchangeRateStored() public view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("exchangeRateStored()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Get cash balance of this cToken in the underlying asset
                             * @return The quantity of underlying asset owned by this contract
                             */
                            function getCash() external view returns (uint) {
                                bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("getCash()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                              * @notice Applies accrued interest to total borrows and reserves.
                              * @dev This calculates interest accrued from the last checkpointed block
                              *      up to the current block and writes new checkpoint to storage.
                              */
                            function accrueInterest() public returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("accrueInterest()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Transfers collateral tokens (this market) to the liquidator.
                             * @dev Will fail unless called by another cToken during the process of liquidation.
                             *  Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter.
                             * @param liquidator The account receiving seized collateral
                             * @param borrower The account having collateral seized
                             * @param seizeTokens The number of cTokens to seize
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("seize(address,address,uint256)", liquidator, borrower, seizeTokens));
                                return abi.decode(data, (uint));
                            }
                        
                            /*** Admin Functions ***/
                        
                            /**
                              * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
                              * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
                              * @param newPendingAdmin New pending admin.
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                              * @notice Sets a new comptroller for the market
                              * @dev Admin function to set a new comptroller
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_setComptroller(address)", newComptroller));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                              * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
                              * @dev Admin function to accrue interest and set a new reserve factor
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_setReserveFactor(uint256)", newReserveFactorMantissa));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                              * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
                              * @dev Admin function for pending admin to accept role and update admin
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _acceptAdmin() external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_acceptAdmin()"));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Accrues interest and adds reserves by transferring from admin
                             * @param addAmount Amount of reserves to add
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _addReserves(uint addAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_addReserves(uint256)", addAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Accrues interest and reduces reserves by transferring to admin
                             * @param reduceAmount Amount of reduction to reserves
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _reduceReserves(uint reduceAmount) external returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_reduceReserves(uint256)", reduceAmount));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh
                             * @dev Admin function to accrue interest and update the interest rate model
                             * @param newInterestRateModel the new interest rate model to use
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) {
                                bytes memory data = delegateToImplementation(abi.encodeWithSignature("_setInterestRateModel(address)", newInterestRateModel));
                                return abi.decode(data, (uint));
                            }
                        
                            /**
                             * @notice Internal method to delegate execution to another contract
                             * @dev It returns to the external caller whatever the implementation returns or forwards reverts
                             * @param callee The contract to delegatecall
                             * @param data The raw data to delegatecall
                             * @return The returned bytes from the delegatecall
                             */
                            function delegateTo(address callee, bytes memory data) internal returns (bytes memory) {
                                (bool success, bytes memory returnData) = callee.delegatecall(data);
                                assembly {
                                    if eq(success, 0) {
                                        revert(add(returnData, 0x20), returndatasize)
                                    }
                                }
                                return returnData;
                            }
                        
                            /**
                             * @notice Delegates execution to the implementation contract
                             * @dev It returns to the external caller whatever the implementation returns or forwards reverts
                             * @param data The raw data to delegatecall
                             * @return The returned bytes from the delegatecall
                             */
                            function delegateToImplementation(bytes memory data) public returns (bytes memory) {
                                return delegateTo(implementation, data);
                            }
                        
                            /**
                             * @notice Delegates execution to an implementation contract
                             * @dev It returns to the external caller whatever the implementation returns or forwards reverts
                             *  There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop.
                             * @param data The raw data to delegatecall
                             * @return The returned bytes from the delegatecall
                             */
                            function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) {
                                (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data));
                                assembly {
                                    if eq(success, 0) {
                                        revert(add(returnData, 0x20), returndatasize)
                                    }
                                }
                                return abi.decode(returnData, (bytes));
                            }
                        
                            /**
                             * @notice Delegates execution to an implementation contract
                             * @dev It returns to the external caller whatever the implementation returns or forwards reverts
                             */
                            function () external payable {
                                require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback");
                        
                                // delegate all other functions to current implementation
                                (bool success, ) = implementation.delegatecall(msg.data);
                        
                                assembly {
                                    let free_mem_ptr := mload(0x40)
                                    returndatacopy(free_mem_ptr, 0, returndatasize)
                        
                                    switch success
                                    case 0 { revert(free_mem_ptr, returndatasize) }
                                    default { return(free_mem_ptr, returndatasize) }
                                }
                            }
                        }
                        

                        File 5 of 6: CDaiDelegate
                        // File: contracts/ComptrollerInterface.sol
                        
                        pragma solidity ^0.5.12;
                        
                        interface ComptrollerInterface {
                            /**
                             * @notice Marker function used for light validation when updating the comptroller of a market
                             * @dev Implementations should simply return true.
                             * @return true
                             */
                            function isComptroller() external view returns (bool);
                        
                            /*** Assets You Are In ***/
                        
                            function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
                            function exitMarket(address cToken) external returns (uint);
                        
                            /*** Policy Hooks ***/
                        
                            function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint);
                            function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external;
                        
                            function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint);
                            function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external;
                        
                            function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint);
                            function borrowVerify(address cToken, address borrower, uint borrowAmount) external;
                        
                            function repayBorrowAllowed(
                                address cToken,
                                address payer,
                                address borrower,
                                uint repayAmount) external returns (uint);
                            function repayBorrowVerify(
                                address cToken,
                                address payer,
                                address borrower,
                                uint repayAmount,
                                uint borrowerIndex) external;
                        
                            function liquidateBorrowAllowed(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                address liquidator,
                                address borrower,
                                uint repayAmount) external returns (uint);
                            function liquidateBorrowVerify(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                address liquidator,
                                address borrower,
                                uint repayAmount,
                                uint seizeTokens) external;
                        
                            function seizeAllowed(
                                address cTokenCollateral,
                                address cTokenBorrowed,
                                address liquidator,
                                address borrower,
                                uint seizeTokens) external returns (uint);
                            function seizeVerify(
                                address cTokenCollateral,
                                address cTokenBorrowed,
                                address liquidator,
                                address borrower,
                                uint seizeTokens) external;
                        
                            function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint);
                            function transferVerify(address cToken, address src, address dst, uint transferTokens) external;
                        
                            /*** Liquidity/Liquidation Calculations ***/
                        
                            function liquidateCalculateSeizeTokens(
                                address cTokenBorrowed,
                                address cTokenCollateral,
                                uint repayAmount) external view returns (uint, uint);
                        }
                        
                        // File: contracts/InterestRateModel.sol
                        
                        pragma solidity ^0.5.12;
                        
                        /**
                          * @title Compound's InterestRateModel Interface
                          * @author Compound
                          */
                        interface InterestRateModel {
                            /**
                             * @notice Indicator that this is an InterestRateModel contract (for inspection)
                             */
                            function isInterestRateModel() external pure returns (bool);
                        
                            /**
                              * @notice Calculates the current borrow interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @return The borrow rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);
                        
                            /**
                              * @notice Calculates the current supply interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @param reserveFactorMantissa The current reserve factor the market has
                              * @return The supply rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint);
                        
                        }
                        
                        // File: contracts/CTokenInterfaces.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        
                        contract CTokenStorage {
                            /**
                             * @dev Guard variable for re-entrancy checks
                             */
                            bool internal _notEntered;
                        
                            /**
                             * @notice EIP-20 token name for this token
                             */
                            string public name;
                        
                            /**
                             * @notice EIP-20 token symbol for this token
                             */
                            string public symbol;
                        
                            /**
                             * @notice EIP-20 token decimals for this token
                             */
                            uint8 public decimals;
                        
                            /**
                             * @notice Maximum borrow rate that can ever be applied (.0005% / block)
                             */
                        
                            uint internal constant borrowRateMaxMantissa = 0.0005e16;
                        
                            /**
                             * @notice Maximum fraction of interest that can be set aside for reserves
                             */
                            uint internal constant reserveFactorMaxMantissa = 1e18;
                        
                            /**
                             * @notice Administrator for this contract
                             */
                            address payable public admin;
                        
                            /**
                             * @notice Pending administrator for this contract
                             */
                            address payable public pendingAdmin;
                        
                            /**
                             * @notice Contract which oversees inter-cToken operations
                             */
                            ComptrollerInterface public comptroller;
                        
                            /**
                             * @notice Model which tells what the current interest rate should be
                             */
                            InterestRateModel public interestRateModel;
                        
                            /**
                             * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0)
                             */
                            uint internal initialExchangeRateMantissa;
                        
                            /**
                             * @notice Fraction of interest currently set aside for reserves
                             */
                            uint public reserveFactorMantissa;
                        
                            /**
                             * @notice Block number that interest was last accrued at
                             */
                            uint public accrualBlockNumber;
                        
                            /**
                             * @notice Accumulator of the total earned interest rate since the opening of the market
                             */
                            uint public borrowIndex;
                        
                            /**
                             * @notice Total amount of outstanding borrows of the underlying in this market
                             */
                            uint public totalBorrows;
                        
                            /**
                             * @notice Total amount of reserves of the underlying held in this market
                             */
                            uint public totalReserves;
                        
                            /**
                             * @notice Total number of tokens in circulation
                             */
                            uint public totalSupply;
                        
                            /**
                             * @notice Official record of token balances for each account
                             */
                            mapping (address => uint) internal accountTokens;
                        
                            /**
                             * @notice Approved token transfer amounts on behalf of others
                             */
                            mapping (address => mapping (address => uint)) internal transferAllowances;
                        
                            /**
                             * @notice Container for borrow balance information
                             * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
                             * @member interestIndex Global borrowIndex as of the most recent balance-changing action
                             */
                            struct BorrowSnapshot {
                                uint principal;
                                uint interestIndex;
                            }
                        
                            /**
                             * @notice Mapping of account addresses to outstanding borrow balances
                             */
                            mapping(address => BorrowSnapshot) internal accountBorrows;
                        }
                        
                        contract CTokenInterface is CTokenStorage {
                            /**
                             * @notice Indicator that this is a CToken contract (for inspection)
                             */
                            bool public constant isCToken = true;
                        
                        
                            /*** Market Events ***/
                        
                            /**
                             * @notice Event emitted when interest is accrued
                             */
                            event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when tokens are minted
                             */
                            event Mint(address minter, uint mintAmount, uint mintTokens);
                        
                            /**
                             * @notice Event emitted when tokens are redeemed
                             */
                            event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);
                        
                            /**
                             * @notice Event emitted when underlying is borrowed
                             */
                            event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when a borrow is repaid
                             */
                            event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows);
                        
                            /**
                             * @notice Event emitted when a borrow is liquidated
                             */
                            event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens);
                        
                        
                            /*** Admin Events ***/
                        
                            /**
                             * @notice Event emitted when pendingAdmin is changed
                             */
                            event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
                        
                            /**
                             * @notice Event emitted when pendingAdmin is accepted, which means admin is updated
                             */
                            event NewAdmin(address oldAdmin, address newAdmin);
                        
                            /**
                             * @notice Event emitted when comptroller is changed
                             */
                            event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller);
                        
                            /**
                             * @notice Event emitted when interestRateModel is changed
                             */
                            event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);
                        
                            /**
                             * @notice Event emitted when the reserve factor is changed
                             */
                            event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa);
                        
                            /**
                             * @notice Event emitted when the reserves are added
                             */
                            event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves);
                        
                            /**
                             * @notice Event emitted when the reserves are reduced
                             */
                            event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves);
                        
                            /**
                             * @notice EIP20 Transfer event
                             */
                            event Transfer(address indexed from, address indexed to, uint amount);
                        
                            /**
                             * @notice EIP20 Approval event
                             */
                            event Approval(address indexed owner, address indexed spender, uint amount);
                        
                            /**
                             * @notice Failure event
                             */
                            event Failure(uint error, uint info, uint detail);
                        
                        
                            /*** User Interface ***/
                        
                            function transfer(address dst, uint amount) external returns (bool);
                            function transferFrom(address src, address dst, uint amount) external returns (bool);
                            function approve(address spender, uint amount) external returns (bool);
                            function allowance(address owner, address spender) external view returns (uint);
                            function balanceOf(address owner) external view returns (uint);
                            function balanceOfUnderlying(address owner) external returns (uint);
                            function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
                            function borrowRatePerBlock() external view returns (uint);
                            function supplyRatePerBlock() external view returns (uint);
                            function totalBorrowsCurrent() external returns (uint);
                            function borrowBalanceCurrent(address account) external returns (uint);
                            function borrowBalanceStored(address account) public view returns (uint);
                            function exchangeRateCurrent() public returns (uint);
                            function exchangeRateStored() public view returns (uint);
                            function getCash() external view returns (uint);
                            function accrueInterest() public returns (uint);
                            function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint);
                        
                        
                            /*** Admin Functions ***/
                        
                            function _setPendingAdmin(address payable newPendingAdmin) external returns (uint);
                            function _acceptAdmin() external returns (uint);
                            function _setComptroller(ComptrollerInterface newComptroller) public returns (uint);
                            function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint);
                            function _reduceReserves(uint reduceAmount) external returns (uint);
                            function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint);
                        }
                        
                        contract CErc20Storage {
                            /**
                             * @notice Underlying asset for this CToken
                             */
                            address public underlying;
                        }
                        
                        contract CErc20Interface is CErc20Storage {
                        
                            /*** User Interface ***/
                        
                            function mint(uint mintAmount) external returns (uint);
                            function redeem(uint redeemTokens) external returns (uint);
                            function redeemUnderlying(uint redeemAmount) external returns (uint);
                            function borrow(uint borrowAmount) external returns (uint);
                            function repayBorrow(uint repayAmount) external returns (uint);
                            function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
                            function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint);
                        
                        
                            /*** Admin Functions ***/
                        
                            function _addReserves(uint addAmount) external returns (uint);
                        }
                        
                        contract CDelegationStorage {
                            /**
                             * @notice Implementation address for this contract
                             */
                            address public implementation;
                        }
                        
                        contract CDelegatorInterface is CDelegationStorage {
                            /**
                             * @notice Emitted when implementation is changed
                             */
                            event NewImplementation(address oldImplementation, address newImplementation);
                        
                            /**
                             * @notice Called by the admin to update the implementation of the delegator
                             * @param implementation_ The address of the new implementation for delegation
                             * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
                             * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
                             */
                            function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public;
                        }
                        
                        contract CDelegateInterface is CDelegationStorage {
                            /**
                             * @notice Called by the delegator on a delegate to initialize it for duty
                             * @dev Should revert if any issues arise which make it unfit for delegation
                             * @param data The encoded bytes data for any initialization
                             */
                            function _becomeImplementation(bytes memory data) public;
                        
                            /**
                             * @notice Called by the delegator on a delegate to forfeit its responsibility
                             */
                            function _resignImplementation() public;
                        }
                        
                        // File: contracts/ErrorReporter.sol
                        
                        pragma solidity ^0.5.12;
                        
                        contract ComptrollerErrorReporter {
                            enum Error {
                                NO_ERROR,
                                UNAUTHORIZED,
                                COMPTROLLER_MISMATCH,
                                INSUFFICIENT_SHORTFALL,
                                INSUFFICIENT_LIQUIDITY,
                                INVALID_CLOSE_FACTOR,
                                INVALID_COLLATERAL_FACTOR,
                                INVALID_LIQUIDATION_INCENTIVE,
                                MARKET_NOT_ENTERED, // no longer possible
                                MARKET_NOT_LISTED,
                                MARKET_ALREADY_LISTED,
                                MATH_ERROR,
                                NONZERO_BORROW_BALANCE,
                                PRICE_ERROR,
                                REJECTION,
                                SNAPSHOT_ERROR,
                                TOO_MANY_ASSETS,
                                TOO_MUCH_REPAY
                            }
                        
                            enum FailureInfo {
                                ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
                                ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
                                EXIT_MARKET_BALANCE_OWED,
                                EXIT_MARKET_REJECTION,
                                SET_CLOSE_FACTOR_OWNER_CHECK,
                                SET_CLOSE_FACTOR_VALIDATION,
                                SET_COLLATERAL_FACTOR_OWNER_CHECK,
                                SET_COLLATERAL_FACTOR_NO_EXISTS,
                                SET_COLLATERAL_FACTOR_VALIDATION,
                                SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
                                SET_IMPLEMENTATION_OWNER_CHECK,
                                SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
                                SET_LIQUIDATION_INCENTIVE_VALIDATION,
                                SET_MAX_ASSETS_OWNER_CHECK,
                                SET_PENDING_ADMIN_OWNER_CHECK,
                                SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
                                SET_PRICE_ORACLE_OWNER_CHECK,
                                SUPPORT_MARKET_EXISTS,
                                SUPPORT_MARKET_OWNER_CHECK,
                                SET_PAUSE_GUARDIAN_OWNER_CHECK
                            }
                        
                            /**
                              * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
                              * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
                              **/
                            event Failure(uint error, uint info, uint detail);
                        
                            /**
                              * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
                              */
                            function fail(Error err, FailureInfo info) internal returns (uint) {
                                emit Failure(uint(err), uint(info), 0);
                        
                                return uint(err);
                            }
                        
                            /**
                              * @dev use this when reporting an opaque error from an upgradeable collaborator contract
                              */
                            function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
                                emit Failure(uint(err), uint(info), opaqueError);
                        
                                return uint(err);
                            }
                        }
                        
                        contract TokenErrorReporter {
                            enum Error {
                                NO_ERROR,
                                UNAUTHORIZED,
                                BAD_INPUT,
                                COMPTROLLER_REJECTION,
                                COMPTROLLER_CALCULATION_ERROR,
                                INTEREST_RATE_MODEL_ERROR,
                                INVALID_ACCOUNT_PAIR,
                                INVALID_CLOSE_AMOUNT_REQUESTED,
                                INVALID_COLLATERAL_FACTOR,
                                MATH_ERROR,
                                MARKET_NOT_FRESH,
                                MARKET_NOT_LISTED,
                                TOKEN_INSUFFICIENT_ALLOWANCE,
                                TOKEN_INSUFFICIENT_BALANCE,
                                TOKEN_INSUFFICIENT_CASH,
                                TOKEN_TRANSFER_IN_FAILED,
                                TOKEN_TRANSFER_OUT_FAILED
                            }
                        
                            /*
                             * Note: FailureInfo (but not Error) is kept in alphabetical order
                             *       This is because FailureInfo grows significantly faster, and
                             *       the order of Error has some meaning, while the order of FailureInfo
                             *       is entirely arbitrary.
                             */
                            enum FailureInfo {
                                ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
                                ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED,
                                ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED,
                                ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED,
                                ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED,
                                ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED,
                                ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
                                BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
                                BORROW_ACCRUE_INTEREST_FAILED,
                                BORROW_CASH_NOT_AVAILABLE,
                                BORROW_FRESHNESS_CHECK,
                                BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
                                BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
                                BORROW_MARKET_NOT_LISTED,
                                BORROW_COMPTROLLER_REJECTION,
                                LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
                                LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
                                LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
                                LIQUIDATE_COMPTROLLER_REJECTION,
                                LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED,
                                LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX,
                                LIQUIDATE_CLOSE_AMOUNT_IS_ZERO,
                                LIQUIDATE_FRESHNESS_CHECK,
                                LIQUIDATE_LIQUIDATOR_IS_BORROWER,
                                LIQUIDATE_REPAY_BORROW_FRESH_FAILED,
                                LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED,
                                LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
                                LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
                                LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
                                LIQUIDATE_SEIZE_TOO_MUCH,
                                MINT_ACCRUE_INTEREST_FAILED,
                                MINT_COMPTROLLER_REJECTION,
                                MINT_EXCHANGE_CALCULATION_FAILED,
                                MINT_EXCHANGE_RATE_READ_FAILED,
                                MINT_FRESHNESS_CHECK,
                                MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
                                MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
                                MINT_TRANSFER_IN_FAILED,
                                MINT_TRANSFER_IN_NOT_POSSIBLE,
                                REDEEM_ACCRUE_INTEREST_FAILED,
                                REDEEM_COMPTROLLER_REJECTION,
                                REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED,
                                REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED,
                                REDEEM_EXCHANGE_RATE_READ_FAILED,
                                REDEEM_FRESHNESS_CHECK,
                                REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
                                REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
                                REDEEM_TRANSFER_OUT_NOT_POSSIBLE,
                                REDUCE_RESERVES_ACCRUE_INTEREST_FAILED,
                                REDUCE_RESERVES_ADMIN_CHECK,
                                REDUCE_RESERVES_CASH_NOT_AVAILABLE,
                                REDUCE_RESERVES_FRESH_CHECK,
                                REDUCE_RESERVES_VALIDATION,
                                REPAY_BEHALF_ACCRUE_INTEREST_FAILED,
                                REPAY_BORROW_ACCRUE_INTEREST_FAILED,
                                REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
                                REPAY_BORROW_COMPTROLLER_REJECTION,
                                REPAY_BORROW_FRESHNESS_CHECK,
                                REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
                                REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
                                REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
                                SET_COLLATERAL_FACTOR_OWNER_CHECK,
                                SET_COLLATERAL_FACTOR_VALIDATION,
                                SET_COMPTROLLER_OWNER_CHECK,
                                SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED,
                                SET_INTEREST_RATE_MODEL_FRESH_CHECK,
                                SET_INTEREST_RATE_MODEL_OWNER_CHECK,
                                SET_MAX_ASSETS_OWNER_CHECK,
                                SET_ORACLE_MARKET_NOT_LISTED,
                                SET_PENDING_ADMIN_OWNER_CHECK,
                                SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED,
                                SET_RESERVE_FACTOR_ADMIN_CHECK,
                                SET_RESERVE_FACTOR_FRESH_CHECK,
                                SET_RESERVE_FACTOR_BOUNDS_CHECK,
                                TRANSFER_COMPTROLLER_REJECTION,
                                TRANSFER_NOT_ALLOWED,
                                TRANSFER_NOT_ENOUGH,
                                TRANSFER_TOO_MUCH,
                                ADD_RESERVES_ACCRUE_INTEREST_FAILED,
                                ADD_RESERVES_FRESH_CHECK,
                                ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE
                            }
                        
                            /**
                              * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
                              * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
                              **/
                            event Failure(uint error, uint info, uint detail);
                        
                            /**
                              * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
                              */
                            function fail(Error err, FailureInfo info) internal returns (uint) {
                                emit Failure(uint(err), uint(info), 0);
                        
                                return uint(err);
                            }
                        
                            /**
                              * @dev use this when reporting an opaque error from an upgradeable collaborator contract
                              */
                            function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
                                emit Failure(uint(err), uint(info), opaqueError);
                        
                                return uint(err);
                            }
                        }
                        
                        // File: contracts/CarefulMath.sol
                        
                        pragma solidity ^0.5.12;
                        
                        /**
                          * @title Careful Math
                          * @author Compound
                          * @notice Derived from OpenZeppelin's SafeMath library
                          *         https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
                          */
                        contract CarefulMath {
                        
                            /**
                             * @dev Possible error codes that we can return
                             */
                            enum MathError {
                                NO_ERROR,
                                DIVISION_BY_ZERO,
                                INTEGER_OVERFLOW,
                                INTEGER_UNDERFLOW
                            }
                        
                            /**
                            * @dev Multiplies two numbers, returns an error on overflow.
                            */
                            function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
                                if (a == 0) {
                                    return (MathError.NO_ERROR, 0);
                                }
                        
                                uint c = a * b;
                        
                                if (c / a != b) {
                                    return (MathError.INTEGER_OVERFLOW, 0);
                                } else {
                                    return (MathError.NO_ERROR, c);
                                }
                            }
                        
                            /**
                            * @dev Integer division of two numbers, truncating the quotient.
                            */
                            function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
                                if (b == 0) {
                                    return (MathError.DIVISION_BY_ZERO, 0);
                                }
                        
                                return (MathError.NO_ERROR, a / b);
                            }
                        
                            /**
                            * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
                            */
                            function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
                                if (b <= a) {
                                    return (MathError.NO_ERROR, a - b);
                                } else {
                                    return (MathError.INTEGER_UNDERFLOW, 0);
                                }
                            }
                        
                            /**
                            * @dev Adds two numbers, returns an error on overflow.
                            */
                            function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
                                uint c = a + b;
                        
                                if (c >= a) {
                                    return (MathError.NO_ERROR, c);
                                } else {
                                    return (MathError.INTEGER_OVERFLOW, 0);
                                }
                            }
                        
                            /**
                            * @dev add a and b and then subtract c
                            */
                            function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
                                (MathError err0, uint sum) = addUInt(a, b);
                        
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, 0);
                                }
                        
                                return subUInt(sum, c);
                            }
                        }
                        
                        // File: contracts/Exponential.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        /**
                         * @title Exponential module for storing fixed-precision decimals
                         * @author Compound
                         * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
                         *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
                         *         `Exp({mantissa: 5100000000000000000})`.
                         */
                        contract Exponential is CarefulMath {
                            uint constant expScale = 1e18;
                            uint constant halfExpScale = expScale/2;
                            uint constant mantissaOne = expScale;
                        
                            struct Exp {
                                uint mantissa;
                            }
                        
                            /**
                             * @dev Creates an exponential from numerator and denominator values.
                             *      Note: Returns an error if (`num` * 10e18) > MAX_INT,
                             *            or if `denom` is zero.
                             */
                            function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
                                (MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, Exp({mantissa: 0}));
                                }
                        
                                (MathError err1, uint rational) = divUInt(scaledNumerator, denom);
                                if (err1 != MathError.NO_ERROR) {
                                    return (err1, Exp({mantissa: 0}));
                                }
                        
                                return (MathError.NO_ERROR, Exp({mantissa: rational}));
                            }
                        
                            /**
                             * @dev Adds two exponentials, returning a new exponential.
                             */
                            function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
                                (MathError error, uint result) = addUInt(a.mantissa, b.mantissa);
                        
                                return (error, Exp({mantissa: result}));
                            }
                        
                            /**
                             * @dev Subtracts two exponentials, returning a new exponential.
                             */
                            function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
                                (MathError error, uint result) = subUInt(a.mantissa, b.mantissa);
                        
                                return (error, Exp({mantissa: result}));
                            }
                        
                            /**
                             * @dev Multiply an Exp by a scalar, returning a new Exp.
                             */
                            function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
                                (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, Exp({mantissa: 0}));
                                }
                        
                                return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
                            }
                        
                            /**
                             * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
                             */
                            function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
                                (MathError err, Exp memory product) = mulScalar(a, scalar);
                                if (err != MathError.NO_ERROR) {
                                    return (err, 0);
                                }
                        
                                return (MathError.NO_ERROR, truncate(product));
                            }
                        
                            /**
                             * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
                             */
                            function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
                                (MathError err, Exp memory product) = mulScalar(a, scalar);
                                if (err != MathError.NO_ERROR) {
                                    return (err, 0);
                                }
                        
                                return addUInt(truncate(product), addend);
                            }
                        
                            /**
                             * @dev Divide an Exp by a scalar, returning a new Exp.
                             */
                            function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
                                (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, Exp({mantissa: 0}));
                                }
                        
                                return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa}));
                            }
                        
                            /**
                             * @dev Divide a scalar by an Exp, returning a new Exp.
                             */
                            function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) {
                                /*
                                  We are doing this as:
                                  getExp(mulUInt(expScale, scalar), divisor.mantissa)
                        
                                  How it works:
                                  Exp = a / b;
                                  Scalar = s;
                                  `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
                                */
                                (MathError err0, uint numerator) = mulUInt(expScale, scalar);
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, Exp({mantissa: 0}));
                                }
                                return getExp(numerator, divisor.mantissa);
                            }
                        
                            /**
                             * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer.
                             */
                            function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
                                (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
                                if (err != MathError.NO_ERROR) {
                                    return (err, 0);
                                }
                        
                                return (MathError.NO_ERROR, truncate(fraction));
                            }
                        
                            /**
                             * @dev Multiplies two exponentials, returning a new exponential.
                             */
                            function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
                        
                                (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
                                if (err0 != MathError.NO_ERROR) {
                                    return (err0, Exp({mantissa: 0}));
                                }
                        
                                // We add half the scale before dividing so that we get rounding instead of truncation.
                                //  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
                                // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
                                (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
                                if (err1 != MathError.NO_ERROR) {
                                    return (err1, Exp({mantissa: 0}));
                                }
                        
                                (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
                                // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
                                assert(err2 == MathError.NO_ERROR);
                        
                                return (MathError.NO_ERROR, Exp({mantissa: product}));
                            }
                        
                            /**
                             * @dev Multiplies two exponentials given their mantissas, returning a new exponential.
                             */
                            function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) {
                                return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
                            }
                        
                            /**
                             * @dev Multiplies three exponentials, returning a new exponential.
                             */
                            function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
                                (MathError err, Exp memory ab) = mulExp(a, b);
                                if (err != MathError.NO_ERROR) {
                                    return (err, ab);
                                }
                                return mulExp(ab, c);
                            }
                        
                            /**
                             * @dev Divides two exponentials, returning a new exponential.
                             *     (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
                             *  which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
                             */
                            function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
                                return getExp(a.mantissa, b.mantissa);
                            }
                        
                            /**
                             * @dev Truncates the given exp to a whole number value.
                             *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
                             */
                            function truncate(Exp memory exp) pure internal returns (uint) {
                                // Note: We are not using careful math here as we're performing a division that cannot fail
                                return exp.mantissa / expScale;
                            }
                        
                            /**
                             * @dev Checks if first Exp is less than second Exp.
                             */
                            function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
                                return left.mantissa < right.mantissa;
                            }
                        
                            /**
                             * @dev Checks if left Exp <= right Exp.
                             */
                            function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
                                return left.mantissa <= right.mantissa;
                            }
                        
                            /**
                             * @dev Checks if left Exp > right Exp.
                             */
                            function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
                                return left.mantissa > right.mantissa;
                            }
                        
                            /**
                             * @dev returns true if Exp is exactly zero
                             */
                            function isZeroExp(Exp memory value) pure internal returns (bool) {
                                return value.mantissa == 0;
                            }
                        }
                        
                        // File: contracts/EIP20Interface.sol
                        
                        pragma solidity ^0.5.12;
                        
                        /**
                         * @title ERC 20 Token Standard Interface
                         *  https://eips.ethereum.org/EIPS/eip-20
                         */
                        interface EIP20Interface {
                        
                            /**
                              * @notice Get the total number of tokens in circulation
                              * @return The supply of tokens
                              */
                            function totalSupply() external view returns (uint256);
                        
                            /**
                             * @notice Gets the balance of the specified address
                             * @param owner The address from which the balance will be retrieved
                             * @return The balance
                             */
                            function balanceOf(address owner) external view returns (uint256 balance);
                        
                            /**
                              * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                              * @param dst The address of the destination account
                              * @param amount The number of tokens to transfer
                              * @return Whether or not the transfer succeeded
                              */
                            function transfer(address dst, uint256 amount) external returns (bool success);
                        
                            /**
                              * @notice Transfer `amount` tokens from `src` to `dst`
                              * @param src The address of the source account
                              * @param dst The address of the destination account
                              * @param amount The number of tokens to transfer
                              * @return Whether or not the transfer succeeded
                              */
                            function transferFrom(address src, address dst, uint256 amount) external returns (bool success);
                        
                            /**
                              * @notice Approve `spender` to transfer up to `amount` from `src`
                              * @dev This will overwrite the approval amount for `spender`
                              *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                              * @param spender The address of the account which may transfer tokens
                              * @param amount The number of tokens that are approved (-1 means infinite)
                              * @return Whether or not the approval succeeded
                              */
                            function approve(address spender, uint256 amount) external returns (bool success);
                        
                            /**
                              * @notice Get the current allowance from `owner` for `spender`
                              * @param owner The address of the account which owns the tokens to be spent
                              * @param spender The address of the account which may transfer tokens
                              * @return The number of tokens allowed to be spent (-1 means infinite)
                              */
                            function allowance(address owner, address spender) external view returns (uint256 remaining);
                        
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                        }
                        
                        // File: contracts/EIP20NonStandardInterface.sol
                        
                        pragma solidity ^0.5.12;
                        
                        /**
                         * @title EIP20NonStandardInterface
                         * @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
                         *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
                         */
                        interface EIP20NonStandardInterface {
                        
                            /**
                             * @notice Get the total number of tokens in circulation
                             * @return The supply of tokens
                             */
                            function totalSupply() external view returns (uint256);
                        
                            /**
                             * @notice Gets the balance of the specified address
                             * @param owner The address from which the balance will be retrieved
                             * @return The balance
                             */
                            function balanceOf(address owner) external view returns (uint256 balance);
                        
                            ///
                            /// !!!!!!!!!!!!!!
                            /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
                            /// !!!!!!!!!!!!!!
                            ///
                        
                            /**
                              * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                              * @param dst The address of the destination account
                              * @param amount The number of tokens to transfer
                              */
                            function transfer(address dst, uint256 amount) external;
                        
                            ///
                            /// !!!!!!!!!!!!!!
                            /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
                            /// !!!!!!!!!!!!!!
                            ///
                        
                            /**
                              * @notice Transfer `amount` tokens from `src` to `dst`
                              * @param src The address of the source account
                              * @param dst The address of the destination account
                              * @param amount The number of tokens to transfer
                              */
                            function transferFrom(address src, address dst, uint256 amount) external;
                        
                            /**
                              * @notice Approve `spender` to transfer up to `amount` from `src`
                              * @dev This will overwrite the approval amount for `spender`
                              *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                              * @param spender The address of the account which may transfer tokens
                              * @param amount The number of tokens that are approved
                              * @return Whether or not the approval succeeded
                              */
                            function approve(address spender, uint256 amount) external returns (bool success);
                        
                            /**
                              * @notice Get the current allowance from `owner` for `spender`
                              * @param owner The address of the account which owns the tokens to be spent
                              * @param spender The address of the account which may transfer tokens
                              * @return The number of tokens allowed to be spent
                              */
                            function allowance(address owner, address spender) external view returns (uint256 remaining);
                        
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                        }
                        
                        // File: contracts/CToken.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        
                        
                        
                        
                        
                        
                        /**
                         * @title Compound's CToken Contract
                         * @notice Abstract base for CTokens
                         * @author Compound
                         */
                        contract CToken is CTokenInterface, Exponential, TokenErrorReporter {
                            /**
                             * @notice Initialize the money market
                             * @param comptroller_ The address of the Comptroller
                             * @param interestRateModel_ The address of the interest rate model
                             * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
                             * @param name_ EIP-20 name of this token
                             * @param symbol_ EIP-20 symbol of this token
                             * @param decimals_ EIP-20 decimal precision of this token
                             */
                            function initialize(ComptrollerInterface comptroller_,
                                                InterestRateModel interestRateModel_,
                                                uint initialExchangeRateMantissa_,
                                                string memory name_,
                                                string memory symbol_,
                                                uint8 decimals_) public {
                                require(msg.sender == admin, "only admin may initialize the market");
                                require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");
                        
                                // Set initial exchange rate
                                initialExchangeRateMantissa = initialExchangeRateMantissa_;
                                require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
                        
                                // Set the comptroller
                                uint err = _setComptroller(comptroller_);
                                require(err == uint(Error.NO_ERROR), "setting comptroller failed");
                        
                                // Initialize block number and borrow index (block number mocks depend on comptroller being set)
                                accrualBlockNumber = getBlockNumber();
                                borrowIndex = mantissaOne;
                        
                                // Set the interest rate model (depends on block number / borrow index)
                                err = _setInterestRateModelFresh(interestRateModel_);
                                require(err == uint(Error.NO_ERROR), "setting interest rate model failed");
                        
                                name = name_;
                                symbol = symbol_;
                                decimals = decimals_;
                        
                                // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
                                _notEntered = true;
                            }
                        
                            /**
                             * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
                             * @dev Called by both `transfer` and `transferFrom` internally
                             * @param spender The address of the account performing the transfer
                             * @param src The address of the source account
                             * @param dst The address of the destination account
                             * @param tokens The number of tokens to transfer
                             * @return Whether or not the transfer succeeded
                             */
                            function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) {
                                /* Fail if transfer not allowed */
                                uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens);
                                if (allowed != 0) {
                                    return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed);
                                }
                        
                                /* Do not allow self-transfers */
                                if (src == dst) {
                                    return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED);
                                }
                        
                                /* Get the allowance, infinite for the account owner */
                                uint startingAllowance = 0;
                                if (spender == src) {
                                    startingAllowance = uint(-1);
                                } else {
                                    startingAllowance = transferAllowances[src][spender];
                                }
                        
                                /* Do the calculations, checking for {under,over}flow */
                                MathError mathErr;
                                uint allowanceNew;
                                uint srcTokensNew;
                                uint dstTokensNew;
                        
                                (mathErr, allowanceNew) = subUInt(startingAllowance, tokens);
                                if (mathErr != MathError.NO_ERROR) {
                                    return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ALLOWED);
                                }
                        
                                (mathErr, srcTokensNew) = subUInt(accountTokens[src], tokens);
                                if (mathErr != MathError.NO_ERROR) {
                                    return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH);
                                }
                        
                                (mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens);
                                if (mathErr != MathError.NO_ERROR) {
                                    return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                accountTokens[src] = srcTokensNew;
                                accountTokens[dst] = dstTokensNew;
                        
                                /* Eat some of the allowance (if necessary) */
                                if (startingAllowance != uint(-1)) {
                                    transferAllowances[src][spender] = allowanceNew;
                                }
                        
                                /* We emit a Transfer event */
                                emit Transfer(src, dst, tokens);
                        
                                comptroller.transferVerify(address(this), src, dst, tokens);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                             * @param dst The address of the destination account
                             * @param amount The number of tokens to transfer
                             * @return Whether or not the transfer succeeded
                             */
                            function transfer(address dst, uint256 amount) external nonReentrant returns (bool) {
                                return transferTokens(msg.sender, msg.sender, dst, amount) == uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Transfer `amount` tokens from `src` to `dst`
                             * @param src The address of the source account
                             * @param dst The address of the destination account
                             * @param amount The number of tokens to transfer
                             * @return Whether or not the transfer succeeded
                             */
                            function transferFrom(address src, address dst, uint256 amount) external nonReentrant returns (bool) {
                                return transferTokens(msg.sender, src, dst, amount) == uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Approve `spender` to transfer up to `amount` from `src`
                             * @dev This will overwrite the approval amount for `spender`
                             *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                             * @param spender The address of the account which may transfer tokens
                             * @param amount The number of tokens that are approved (-1 means infinite)
                             * @return Whether or not the approval succeeded
                             */
                            function approve(address spender, uint256 amount) external returns (bool) {
                                address src = msg.sender;
                                transferAllowances[src][spender] = amount;
                                emit Approval(src, spender, amount);
                                return true;
                            }
                        
                            /**
                             * @notice Get the current allowance from `owner` for `spender`
                             * @param owner The address of the account which owns the tokens to be spent
                             * @param spender The address of the account which may transfer tokens
                             * @return The number of tokens allowed to be spent (-1 means infinite)
                             */
                            function allowance(address owner, address spender) external view returns (uint256) {
                                return transferAllowances[owner][spender];
                            }
                        
                            /**
                             * @notice Get the token balance of the `owner`
                             * @param owner The address of the account to query
                             * @return The number of tokens owned by `owner`
                             */
                            function balanceOf(address owner) external view returns (uint256) {
                                return accountTokens[owner];
                            }
                        
                            /**
                             * @notice Get the underlying balance of the `owner`
                             * @dev This also accrues interest in a transaction
                             * @param owner The address of the account to query
                             * @return The amount of underlying owned by `owner`
                             */
                            function balanceOfUnderlying(address owner) external returns (uint) {
                                Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
                                (MathError mErr, uint balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
                                require(mErr == MathError.NO_ERROR, "balance could not be calculated");
                                return balance;
                            }
                        
                            /**
                             * @notice Get a snapshot of the account's balances, and the cached exchange rate
                             * @dev This is used by comptroller to more efficiently perform liquidity checks.
                             * @param account Address of the account to snapshot
                             * @return (possible error, token balance, borrow balance, exchange rate mantissa)
                             */
                            function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) {
                                uint cTokenBalance = accountTokens[account];
                                uint borrowBalance;
                                uint exchangeRateMantissa;
                        
                                MathError mErr;
                        
                                (mErr, borrowBalance) = borrowBalanceStoredInternal(account);
                                if (mErr != MathError.NO_ERROR) {
                                    return (uint(Error.MATH_ERROR), 0, 0, 0);
                                }
                        
                                (mErr, exchangeRateMantissa) = exchangeRateStoredInternal();
                                if (mErr != MathError.NO_ERROR) {
                                    return (uint(Error.MATH_ERROR), 0, 0, 0);
                                }
                        
                                return (uint(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa);
                            }
                        
                            /**
                             * @dev Function to simply retrieve block number
                             *  This exists mainly for inheriting test contracts to stub this result.
                             */
                            function getBlockNumber() internal view returns (uint) {
                                return block.number;
                            }
                        
                            /**
                             * @notice Returns the current per-block borrow interest rate for this cToken
                             * @return The borrow interest rate per block, scaled by 1e18
                             */
                            function borrowRatePerBlock() external view returns (uint) {
                                return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves);
                            }
                        
                            /**
                             * @notice Returns the current per-block supply interest rate for this cToken
                             * @return The supply interest rate per block, scaled by 1e18
                             */
                            function supplyRatePerBlock() external view returns (uint) {
                                return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa);
                            }
                        
                            /**
                             * @notice Returns the current total borrows plus accrued interest
                             * @return The total borrows with interest
                             */
                            function totalBorrowsCurrent() external nonReentrant returns (uint) {
                                require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
                                return totalBorrows;
                            }
                        
                            /**
                             * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
                             * @param account The address whose balance should be calculated after updating borrowIndex
                             * @return The calculated balance
                             */
                            function borrowBalanceCurrent(address account) external nonReentrant returns (uint) {
                                require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
                                return borrowBalanceStored(account);
                            }
                        
                            /**
                             * @notice Return the borrow balance of account based on stored data
                             * @param account The address whose balance should be calculated
                             * @return The calculated balance
                             */
                            function borrowBalanceStored(address account) public view returns (uint) {
                                (MathError err, uint result) = borrowBalanceStoredInternal(account);
                                require(err == MathError.NO_ERROR, "borrowBalanceStored: borrowBalanceStoredInternal failed");
                                return result;
                            }
                        
                            /**
                             * @notice Return the borrow balance of account based on stored data
                             * @param account The address whose balance should be calculated
                             * @return (error code, the calculated balance or 0 if error code is non-zero)
                             */
                            function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint) {
                                /* Note: we do not assert that the market is up to date */
                                MathError mathErr;
                                uint principalTimesIndex;
                                uint result;
                        
                                /* Get borrowBalance and borrowIndex */
                                BorrowSnapshot storage borrowSnapshot = accountBorrows[account];
                        
                                /* If borrowBalance = 0 then borrowIndex is likely also 0.
                                 * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
                                 */
                                if (borrowSnapshot.principal == 0) {
                                    return (MathError.NO_ERROR, 0);
                                }
                        
                                /* Calculate new borrow balance using the interest index:
                                 *  recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
                                 */
                                (mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex);
                                if (mathErr != MathError.NO_ERROR) {
                                    return (mathErr, 0);
                                }
                        
                                (mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex);
                                if (mathErr != MathError.NO_ERROR) {
                                    return (mathErr, 0);
                                }
                        
                                return (MathError.NO_ERROR, result);
                            }
                        
                            /**
                             * @notice Accrue interest then return the up-to-date exchange rate
                             * @return Calculated exchange rate scaled by 1e18
                             */
                            function exchangeRateCurrent() public nonReentrant returns (uint) {
                                require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed");
                                return exchangeRateStored();
                            }
                        
                            /**
                             * @notice Calculates the exchange rate from the underlying to the CToken
                             * @dev This function does not accrue interest before calculating the exchange rate
                             * @return Calculated exchange rate scaled by 1e18
                             */
                            function exchangeRateStored() public view returns (uint) {
                                (MathError err, uint result) = exchangeRateStoredInternal();
                                require(err == MathError.NO_ERROR, "exchangeRateStored: exchangeRateStoredInternal failed");
                                return result;
                            }
                        
                            /**
                             * @notice Calculates the exchange rate from the underlying to the CToken
                             * @dev This function does not accrue interest before calculating the exchange rate
                             * @return (error code, calculated exchange rate scaled by 1e18)
                             */
                            function exchangeRateStoredInternal() internal view returns (MathError, uint) {
                                if (totalSupply == 0) {
                                    /*
                                     * If there are no tokens minted:
                                     *  exchangeRate = initialExchangeRate
                                     */
                                    return (MathError.NO_ERROR, initialExchangeRateMantissa);
                                } else {
                                    /*
                                     * Otherwise:
                                     *  exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
                                     */
                                    uint totalCash = getCashPrior();
                                    uint cashPlusBorrowsMinusReserves;
                                    Exp memory exchangeRate;
                                    MathError mathErr;
                        
                                    (mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves);
                                    if (mathErr != MathError.NO_ERROR) {
                                        return (mathErr, 0);
                                    }
                        
                                    (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, totalSupply);
                                    if (mathErr != MathError.NO_ERROR) {
                                        return (mathErr, 0);
                                    }
                        
                                    return (MathError.NO_ERROR, exchangeRate.mantissa);
                                }
                            }
                        
                            /**
                             * @notice Get cash balance of this cToken in the underlying asset
                             * @return The quantity of underlying asset owned by this contract
                             */
                            function getCash() external view returns (uint) {
                                return getCashPrior();
                            }
                        
                            struct AccrueInterestLocalVars {
                                MathError mathErr;
                                uint opaqueErr;
                                uint borrowRateMantissa;
                                uint currentBlockNumber;
                                uint blockDelta;
                        
                                Exp simpleInterestFactor;
                        
                                uint interestAccumulated;
                                uint totalBorrowsNew;
                                uint totalReservesNew;
                                uint borrowIndexNew;
                            }
                        
                            /**
                             * @notice Applies accrued interest to total borrows and reserves
                             * @dev This calculates interest accrued from the last checkpointed block
                             *   up to the current block and writes new checkpoint to storage.
                             */
                            function accrueInterest() public returns (uint) {
                                AccrueInterestLocalVars memory vars;
                                uint cashPrior = getCashPrior();
                        
                                /* Calculate the current borrow interest rate */
                                vars.borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, totalBorrows, totalReserves);
                                require(vars.borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
                        
                                /* Remember the initial block number */
                                vars.currentBlockNumber = getBlockNumber();
                        
                                /* Calculate the number of blocks elapsed since the last accrual */
                                (vars.mathErr, vars.blockDelta) = subUInt(vars.currentBlockNumber, accrualBlockNumber);
                                require(vars.mathErr == MathError.NO_ERROR, "could not calculate block delta");
                        
                                /*
                                 * Calculate the interest accumulated into borrows and reserves and the new index:
                                 *  simpleInterestFactor = borrowRate * blockDelta
                                 *  interestAccumulated = simpleInterestFactor * totalBorrows
                                 *  totalBorrowsNew = interestAccumulated + totalBorrows
                                 *  totalReservesNew = interestAccumulated * reserveFactor + totalReserves
                                 *  borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
                                 */
                                (vars.mathErr, vars.simpleInterestFactor) = mulScalar(Exp({mantissa: vars.borrowRateMantissa}), vars.blockDelta);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.interestAccumulated) = mulScalarTruncate(vars.simpleInterestFactor, totalBorrows);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.totalBorrowsNew) = addUInt(vars.interestAccumulated, totalBorrows);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.totalReservesNew) = mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), vars.interestAccumulated, totalReserves);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.borrowIndexNew) = mulScalarTruncateAddUInt(vars.simpleInterestFactor, borrowIndex, borrowIndex);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /* We write the previously calculated values into storage */
                                accrualBlockNumber = vars.currentBlockNumber;
                                borrowIndex = vars.borrowIndexNew;
                                totalBorrows = vars.totalBorrowsNew;
                                totalReserves = vars.totalReservesNew;
                        
                                /* We emit an AccrueInterest event */
                                emit AccrueInterest(cashPrior, vars.interestAccumulated, vars.borrowIndexNew, totalBorrows);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Sender supplies assets into the market and receives cTokens in exchange
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param mintAmount The amount of the underlying asset to supply
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
                             */
                            function mintInternal(uint mintAmount) internal nonReentrant returns (uint, uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
                                    return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0);
                                }
                                // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
                                return mintFresh(msg.sender, mintAmount);
                            }
                        
                            struct MintLocalVars {
                                Error err;
                                MathError mathErr;
                                uint exchangeRateMantissa;
                                uint mintTokens;
                                uint totalSupplyNew;
                                uint accountTokensNew;
                                uint actualMintAmount;
                            }
                        
                            /**
                             * @notice User supplies assets into the market and receives cTokens in exchange
                             * @dev Assumes interest has already been accrued up to the current block
                             * @param minter The address of the account which is supplying the assets
                             * @param mintAmount The amount of the underlying asset to supply
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
                             */
                            function mintFresh(address minter, uint mintAmount) internal returns (uint, uint) {
                                /* Fail if mint not allowed */
                                uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount);
                                if (allowed != 0) {
                                    return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0);
                                }
                        
                                /* Verify market's block number equals current block number */
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0);
                                }
                        
                                MintLocalVars memory vars;
                        
                                /* Fail if checkTransferIn fails */
                                vars.err = checkTransferIn(minter, mintAmount);
                                if (vars.err != Error.NO_ERROR) {
                                    return (fail(vars.err, FailureInfo.MINT_TRANSFER_IN_NOT_POSSIBLE), 0);
                                }
                        
                                (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return (failOpaque(Error.MATH_ERROR, FailureInfo.MINT_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr)), 0);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /*
                                 *  We call `doTransferIn` for the minter and the mintAmount.
                                 *  Note: The cToken must handle variations between ERC-20 and ETH underlying.
                                 *  `doTransferIn` reverts if anything goes wrong, since we can't be sure if
                                 *  side-effects occurred. The function returns the amount actually transferred,
                                 *  in case of a fee. On success, the cToken holds an additional `actualMintAmount`
                                 *  of cash.
                                 */
                                vars.actualMintAmount = doTransferIn(minter, mintAmount);
                        
                                /*
                                 * We get the current exchange rate and calculate the number of cTokens to be minted:
                                 *  mintTokens = actualMintAmount / exchangeRate
                                 */
                        
                                (vars.mathErr, vars.mintTokens) = divScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa}));
                                require(vars.mathErr == MathError.NO_ERROR, "MINT_EXCHANGE_CALCULATION_FAILED");
                        
                                /*
                                 * We calculate the new total supply of cTokens and minter token balance, checking for overflow:
                                 *  totalSupplyNew = totalSupply + mintTokens
                                 *  accountTokensNew = accountTokens[minter] + mintTokens
                                 */
                                (vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens);
                                require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED");
                        
                                (vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens);
                                require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED");
                        
                                /* We write previously calculated values into storage */
                                totalSupply = vars.totalSupplyNew;
                                accountTokens[minter] = vars.accountTokensNew;
                        
                                /* We emit a Mint event, and a Transfer event */
                                emit Mint(minter, vars.actualMintAmount, vars.mintTokens);
                                emit Transfer(address(this), minter, vars.mintTokens);
                        
                                /* We call the defense hook */
                                comptroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens);
                        
                                return (uint(Error.NO_ERROR), vars.actualMintAmount);
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for the underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemTokens The number of cTokens to redeem into underlying
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeemInternal(uint redeemTokens) internal nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
                                    return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
                                }
                                // redeemFresh emits redeem-specific logs on errors, so we don't need to
                                return redeemFresh(msg.sender, redeemTokens, 0);
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemAmount The amount of underlying to receive from redeeming cTokens
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
                                    return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
                                }
                                // redeemFresh emits redeem-specific logs on errors, so we don't need to
                                return redeemFresh(msg.sender, 0, redeemAmount);
                            }
                        
                            struct RedeemLocalVars {
                                Error err;
                                MathError mathErr;
                                uint exchangeRateMantissa;
                                uint redeemTokens;
                                uint redeemAmount;
                                uint totalSupplyNew;
                                uint accountTokensNew;
                            }
                        
                            /**
                             * @notice User redeems cTokens in exchange for the underlying asset
                             * @dev Assumes interest has already been accrued up to the current block
                             * @param redeemer The address of the account which is redeeming the tokens
                             * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
                             * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) {
                                require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
                        
                                RedeemLocalVars memory vars;
                        
                                /* exchangeRate = invoke Exchange Rate Stored() */
                                (vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_RATE_READ_FAILED, uint(vars.mathErr));
                                }
                        
                                /* If redeemTokensIn > 0: */
                                if (redeemTokensIn > 0) {
                                    /*
                                     * We calculate the exchange rate and the amount of underlying to be redeemed:
                                     *  redeemTokens = redeemTokensIn
                                     *  redeemAmount = redeemTokensIn x exchangeRateCurrent
                                     */
                                    vars.redeemTokens = redeemTokensIn;
                        
                                    (vars.mathErr, vars.redeemAmount) = mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn);
                                    if (vars.mathErr != MathError.NO_ERROR) {
                                        return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint(vars.mathErr));
                                    }
                                } else {
                                    /*
                                     * We get the current exchange rate and calculate the amount to be redeemed:
                                     *  redeemTokens = redeemAmountIn / exchangeRate
                                     *  redeemAmount = redeemAmountIn
                                     */
                        
                                    (vars.mathErr, vars.redeemTokens) = divScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa}));
                                    if (vars.mathErr != MathError.NO_ERROR) {
                                        return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, uint(vars.mathErr));
                                    }
                        
                                    vars.redeemAmount = redeemAmountIn;
                                }
                        
                                /* Fail if redeem not allowed */
                                uint allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens);
                                if (allowed != 0) {
                                    return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed);
                                }
                        
                                /* Verify market's block number equals current block number */
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK);
                                }
                        
                                /*
                                 * We calculate the new total supply and redeemer balance, checking for underflow:
                                 *  totalSupplyNew = totalSupply - redeemTokens
                                 *  accountTokensNew = accountTokens[redeemer] - redeemTokens
                                 */
                                (vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                /* Fail gracefully if protocol has insufficient cash */
                                if (getCashPrior() < vars.redeemAmount) {
                                    return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /*
                                 * We invoke doTransferOut for the redeemer and the redeemAmount.
                                 *  Note: The cToken must handle variations between ERC-20 and ETH underlying.
                                 *  On success, the cToken has redeemAmount less of cash.
                                 *  doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
                                 */
                                doTransferOut(redeemer, vars.redeemAmount);
                        
                                /* We write previously calculated values into storage */
                                totalSupply = vars.totalSupplyNew;
                                accountTokens[redeemer] = vars.accountTokensNew;
                        
                                /* We emit a Transfer event, and a Redeem event */
                                emit Transfer(redeemer, address(this), vars.redeemTokens);
                                emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens);
                        
                                /* We call the defense hook */
                                comptroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                              * @notice Sender borrows assets from the protocol to their own address
                              * @param borrowAmount The amount of the underlying asset to borrow
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function borrowInternal(uint borrowAmount) internal nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
                                    return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED);
                                }
                                // borrowFresh emits borrow-specific logs on errors, so we don't need to
                                return borrowFresh(msg.sender, borrowAmount);
                            }
                        
                            struct BorrowLocalVars {
                                MathError mathErr;
                                uint accountBorrows;
                                uint accountBorrowsNew;
                                uint totalBorrowsNew;
                            }
                        
                            /**
                              * @notice Users borrow assets from the protocol to their own address
                              * @param borrowAmount The amount of the underlying asset to borrow
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function borrowFresh(address payable borrower, uint borrowAmount) internal returns (uint) {
                                /* Fail if borrow not allowed */
                                uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount);
                                if (allowed != 0) {
                                    return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed);
                                }
                        
                                /* Verify market's block number equals current block number */
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK);
                                }
                        
                                /* Fail gracefully if protocol has insufficient underlying cash */
                                if (getCashPrior() < borrowAmount) {
                                    return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE);
                                }
                        
                                BorrowLocalVars memory vars;
                        
                                /*
                                 * We calculate the new borrower and total borrow balances, failing on overflow:
                                 *  accountBorrowsNew = accountBorrows + borrowAmount
                                 *  totalBorrowsNew = totalBorrows + borrowAmount
                                 */
                                (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.accountBorrowsNew) = addUInt(vars.accountBorrows, borrowAmount);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                (vars.mathErr, vars.totalBorrowsNew) = addUInt(totalBorrows, borrowAmount);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, uint(vars.mathErr));
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /*
                                 * We invoke doTransferOut for the borrower and the borrowAmount.
                                 *  Note: The cToken must handle variations between ERC-20 and ETH underlying.
                                 *  On success, the cToken borrowAmount less of cash.
                                 *  doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
                                 */
                                doTransferOut(borrower, borrowAmount);
                        
                                /* We write the previously calculated values into storage */
                                accountBorrows[borrower].principal = vars.accountBorrowsNew;
                                accountBorrows[borrower].interestIndex = borrowIndex;
                                totalBorrows = vars.totalBorrowsNew;
                        
                                /* We emit a Borrow event */
                                emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);
                        
                                /* We call the defense hook */
                                comptroller.borrowVerify(address(this), borrower, borrowAmount);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Sender repays their own borrow
                             * @param repayAmount The amount to repay
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
                             */
                            function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint, uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
                                    return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0);
                                }
                                // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
                                return repayBorrowFresh(msg.sender, msg.sender, repayAmount);
                            }
                        
                            /**
                             * @notice Sender repays a borrow belonging to borrower
                             * @param borrower the account with the debt being payed off
                             * @param repayAmount The amount to repay
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
                             */
                            function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint, uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
                                    return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0);
                                }
                                // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
                                return repayBorrowFresh(msg.sender, borrower, repayAmount);
                            }
                        
                            struct RepayBorrowLocalVars {
                                Error err;
                                MathError mathErr;
                                uint repayAmount;
                                uint borrowerIndex;
                                uint accountBorrows;
                                uint accountBorrowsNew;
                                uint totalBorrowsNew;
                                uint actualRepayAmount;
                            }
                        
                            /**
                             * @notice Borrows are repaid by another user (possibly the borrower).
                             * @param payer the account paying off the borrow
                             * @param borrower the account with the debt being payed off
                             * @param repayAmount the amount of undelrying tokens being returned
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
                             */
                            function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal returns (uint, uint) {
                                /* Fail if repayBorrow not allowed */
                                uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount);
                                if (allowed != 0) {
                                    return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0);
                                }
                        
                                /* Verify market's block number equals current block number */
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0);
                                }
                        
                                RepayBorrowLocalVars memory vars;
                        
                                /* We remember the original borrowerIndex for verification purposes */
                                vars.borrowerIndex = accountBorrows[borrower].interestIndex;
                        
                                /* We fetch the amount the borrower owes, with accumulated interest */
                                (vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
                                if (vars.mathErr != MathError.NO_ERROR) {
                                    return (failOpaque(Error.MATH_ERROR, FailureInfo.REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint(vars.mathErr)), 0);
                                }
                        
                                /* If repayAmount == -1, repayAmount = accountBorrows */
                                if (repayAmount == uint(-1)) {
                                    vars.repayAmount = vars.accountBorrows;
                                } else {
                                    vars.repayAmount = repayAmount;
                                }
                        
                                /* Fail if checkTransferIn fails */
                                vars.err = checkTransferIn(payer, vars.repayAmount);
                                if (vars.err != Error.NO_ERROR) {
                                    return (fail(vars.err, FailureInfo.REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE), 0);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /*
                                 * We call doTransferIn for the payer and the repayAmount
                                 *  Note: The cToken must handle variations between ERC-20 and ETH underlying.
                                 *  On success, the cToken holds an additional repayAmount of cash.
                                 *  doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
                                 *   it returns the amount actually transferred, in case of a fee.
                                 */
                                vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount);
                        
                                /*
                                 * We calculate the new borrower and total borrow balances, failing on underflow:
                                 *  accountBorrowsNew = accountBorrows - actualRepayAmount
                                 *  totalBorrowsNew = totalBorrows - actualRepayAmount
                                 */
                                (vars.mathErr, vars.accountBorrowsNew) = subUInt(vars.accountBorrows, vars.actualRepayAmount);
                                require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED");
                        
                                (vars.mathErr, vars.totalBorrowsNew) = subUInt(totalBorrows, vars.actualRepayAmount);
                                require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED");
                        
                                /* We write the previously calculated values into storage */
                                accountBorrows[borrower].principal = vars.accountBorrowsNew;
                                accountBorrows[borrower].interestIndex = borrowIndex;
                                totalBorrows = vars.totalBorrowsNew;
                        
                                /* We emit a RepayBorrow event */
                                emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);
                        
                                /* We call the defense hook */
                                comptroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex);
                        
                                return (uint(Error.NO_ERROR), vars.actualRepayAmount);
                            }
                        
                            /**
                             * @notice The sender liquidates the borrowers collateral.
                             *  The collateral seized is transferred to the liquidator.
                             * @param borrower The borrower of this cToken to be liquidated
                             * @param cTokenCollateral The market in which to seize collateral from the borrower
                             * @param repayAmount The amount of the underlying borrowed asset to repay
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
                             */
                            function liquidateBorrowInternal(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal nonReentrant returns (uint, uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
                                    return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0);
                                }
                        
                                error = cTokenCollateral.accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
                                    return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0);
                                }
                        
                                // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
                                return liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral);
                            }
                        
                            /**
                             * @notice The liquidator liquidates the borrowers collateral.
                             *  The collateral seized is transferred to the liquidator.
                             * @param borrower The borrower of this cToken to be liquidated
                             * @param liquidator The address repaying the borrow and seizing collateral
                             * @param cTokenCollateral The market in which to seize collateral from the borrower
                             * @param repayAmount The amount of the underlying borrowed asset to repay
                             * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
                             */
                            function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal returns (uint, uint) {
                                /* Fail if liquidate not allowed */
                                uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount);
                                if (allowed != 0) {
                                    return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_COMPTROLLER_REJECTION, allowed), 0);
                                }
                        
                                /* Verify market's block number equals current block number */
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_FRESHNESS_CHECK), 0);
                                }
                        
                                /* Verify cTokenCollateral market's block number equals current block number */
                                if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
                                    return (fail(Error.MARKET_NOT_FRESH, FailureInfo.LIQUIDATE_COLLATERAL_FRESHNESS_CHECK), 0);
                                }
                        
                                /* Fail if borrower = liquidator */
                                if (borrower == liquidator) {
                                    return (fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_LIQUIDATOR_IS_BORROWER), 0);
                                }
                        
                                /* Fail if repayAmount = 0 */
                                if (repayAmount == 0) {
                                    return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_ZERO), 0);
                                }
                        
                                /* Fail if repayAmount = -1 */
                                if (repayAmount == uint(-1)) {
                                    return (fail(Error.INVALID_CLOSE_AMOUNT_REQUESTED, FailureInfo.LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX), 0);
                                }
                        
                        
                                /* Fail if repayBorrow fails */
                                (uint repayBorrowError, uint actualRepayAmount) = repayBorrowFresh(liquidator, borrower, repayAmount);
                                if (repayBorrowError != uint(Error.NO_ERROR)) {
                                    return (fail(Error(repayBorrowError), FailureInfo.LIQUIDATE_REPAY_BORROW_FRESH_FAILED), 0);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /* We calculate the number of collateral tokens that will be seized */
                                (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(cTokenCollateral), actualRepayAmount);
                                require(amountSeizeError == uint(Error.NO_ERROR), "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
                        
                                /* Revert if borrower collateral token balance < seizeTokens */
                                require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");
                        
                                // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
                                uint seizeError;
                                if (address(cTokenCollateral) == address(this)) {
                                    seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens);
                                } else {
                                    seizeError = cTokenCollateral.seize(liquidator, borrower, seizeTokens);
                                }
                        
                                /* Revert if seize tokens fails (since we cannot be sure of side effects) */
                                require(seizeError == uint(Error.NO_ERROR), "token seizure failed");
                        
                                /* We emit a LiquidateBorrow event */
                                emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens);
                        
                                /* We call the defense hook */
                                comptroller.liquidateBorrowVerify(address(this), address(cTokenCollateral), liquidator, borrower, actualRepayAmount, seizeTokens);
                        
                                return (uint(Error.NO_ERROR), actualRepayAmount);
                            }
                        
                            /**
                             * @notice Transfers collateral tokens (this market) to the liquidator.
                             * @dev Will fail unless called by another cToken during the process of liquidation.
                             *  Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter.
                             * @param liquidator The account receiving seized collateral
                             * @param borrower The account having collateral seized
                             * @param seizeTokens The number of cTokens to seize
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function seize(address liquidator, address borrower, uint seizeTokens) external nonReentrant returns (uint) {
                                return seizeInternal(msg.sender, liquidator, borrower, seizeTokens);
                            }
                        
                            /**
                             * @notice Transfers collateral tokens (this market) to the liquidator.
                             * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken.
                             *  Its absolutely critical to use msg.sender as the seizer cToken and not a parameter.
                             * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken)
                             * @param liquidator The account receiving seized collateral
                             * @param borrower The account having collateral seized
                             * @param seizeTokens The number of cTokens to seize
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint) {
                                /* Fail if seize not allowed */
                                uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens);
                                if (allowed != 0) {
                                    return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.LIQUIDATE_SEIZE_COMPTROLLER_REJECTION, allowed);
                                }
                        
                                /* Fail if borrower = liquidator */
                                if (borrower == liquidator) {
                                    return fail(Error.INVALID_ACCOUNT_PAIR, FailureInfo.LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER);
                                }
                        
                                MathError mathErr;
                                uint borrowerTokensNew;
                                uint liquidatorTokensNew;
                        
                                /*
                                 * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
                                 *  borrowerTokensNew = accountTokens[borrower] - seizeTokens
                                 *  liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
                                 */
                                (mathErr, borrowerTokensNew) = subUInt(accountTokens[borrower], seizeTokens);
                                if (mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED, uint(mathErr));
                                }
                        
                                (mathErr, liquidatorTokensNew) = addUInt(accountTokens[liquidator], seizeTokens);
                                if (mathErr != MathError.NO_ERROR) {
                                    return failOpaque(Error.MATH_ERROR, FailureInfo.LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED, uint(mathErr));
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /* We write the previously calculated values into storage */
                                accountTokens[borrower] = borrowerTokensNew;
                                accountTokens[liquidator] = liquidatorTokensNew;
                        
                                /* Emit a Transfer event */
                                emit Transfer(borrower, liquidator, seizeTokens);
                        
                                /* We call the defense hook */
                                comptroller.seizeVerify(address(this), seizerToken, liquidator, borrower, seizeTokens);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                        
                            /*** Admin Functions ***/
                        
                            /**
                              * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
                              * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
                              * @param newPendingAdmin New pending admin.
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) {
                                // Check caller = admin
                                if (msg.sender != admin) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
                                }
                        
                                // Save current value, if any, for inclusion in log
                                address oldPendingAdmin = pendingAdmin;
                        
                                // Store pendingAdmin with value newPendingAdmin
                                pendingAdmin = newPendingAdmin;
                        
                                // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
                                emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                              * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
                              * @dev Admin function for pending admin to accept role and update admin
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _acceptAdmin() external returns (uint) {
                                // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
                                if (msg.sender != pendingAdmin || msg.sender == address(0)) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK);
                                }
                        
                                // Save current values for inclusion in log
                                address oldAdmin = admin;
                                address oldPendingAdmin = pendingAdmin;
                        
                                // Store admin with value pendingAdmin
                                admin = pendingAdmin;
                        
                                // Clear the pending value
                                pendingAdmin = address(0);
                        
                                emit NewAdmin(oldAdmin, admin);
                                emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                              * @notice Sets a new comptroller for the market
                              * @dev Admin function to set a new comptroller
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setComptroller(ComptrollerInterface newComptroller) public returns (uint) {
                                // Check caller is admin
                                if (msg.sender != admin) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
                                }
                        
                                ComptrollerInterface oldComptroller = comptroller;
                                // Ensure invoke comptroller.isComptroller() returns true
                                require(newComptroller.isComptroller(), "marker method returned false");
                        
                                // Set market's comptroller to newComptroller
                                comptroller = newComptroller;
                        
                                // Emit NewComptroller(oldComptroller, newComptroller)
                                emit NewComptroller(oldComptroller, newComptroller);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                              * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
                              * @dev Admin function to accrue interest and set a new reserve factor
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setReserveFactor(uint newReserveFactorMantissa) external nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed.
                                    return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED);
                                }
                                // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
                                return _setReserveFactorFresh(newReserveFactorMantissa);
                            }
                        
                            /**
                              * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
                              * @dev Admin function to set a new reserve factor
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) {
                                // Check caller is admin
                                if (msg.sender != admin) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK);
                                }
                        
                                // Verify market's block number equals current block number
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK);
                                }
                        
                                // Check newReserveFactor ≤ maxReserveFactor
                                if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
                                    return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK);
                                }
                        
                                uint oldReserveFactorMantissa = reserveFactorMantissa;
                                reserveFactorMantissa = newReserveFactorMantissa;
                        
                                emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice Accrues interest and reduces reserves by transferring from msg.sender
                             * @param addAmount Amount of addition to reserves
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
                                    return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED);
                                }
                        
                                // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
                                (error, ) = _addReservesFresh(addAmount);
                                return error;
                            }
                        
                            /**
                             * @notice Add reserves by transferring from caller
                             * @dev Requires fresh interest accrual
                             * @param addAmount Amount of addition to reserves
                             * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
                             */
                            function _addReservesFresh(uint addAmount) internal returns (uint, uint) {
                                // totalReserves + actualAddAmount
                                uint totalReservesNew;
                                uint actualAddAmount;
                        
                                // We fail gracefully unless market's block number equals current block number
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount);
                                }
                        
                                /* Fail if checkTransferIn fails */
                                Error err = checkTransferIn(msg.sender, addAmount);
                                if (err != Error.NO_ERROR) {
                                    return (fail(err, FailureInfo.ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE), actualAddAmount);
                                }
                        
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                /* actualAddAmount=invoke doTransferIn(msg.sender, addAmount) */
                                actualAddAmount = doTransferIn(msg.sender, addAmount);
                        
                                totalReservesNew = totalReserves + actualAddAmount;
                        
                                /* Revert on overflow */
                                require(totalReservesNew >= totalReserves, "add reserves unexpected overflow");
                        
                                // Store reserves[n+1] = reserves[n] + actualAddAmount
                                totalReserves = totalReservesNew;
                        
                                /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */
                                emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);
                        
                                /* Return (NO_ERROR, actualAddAmount) */
                                return (uint(Error.NO_ERROR), actualAddAmount);
                            }
                        
                        
                            /**
                             * @notice Accrues interest and reduces reserves by transferring to admin
                             * @param reduceAmount Amount of reduction to reserves
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _reduceReserves(uint reduceAmount) external nonReentrant returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
                                    return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED);
                                }
                                // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
                                return _reduceReservesFresh(reduceAmount);
                            }
                        
                            /**
                             * @notice Reduces reserves by transferring to admin
                             * @dev Requires fresh interest accrual
                             * @param reduceAmount Amount of reduction to reserves
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _reduceReservesFresh(uint reduceAmount) internal returns (uint) {
                                // totalReserves - reduceAmount
                                uint totalReservesNew;
                        
                                // Check caller is admin
                                if (msg.sender != admin) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK);
                                }
                        
                                // We fail gracefully unless market's block number equals current block number
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK);
                                }
                        
                                // Fail gracefully if protocol has insufficient underlying cash
                                if (getCashPrior() < reduceAmount) {
                                    return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE);
                                }
                        
                                // Check reduceAmount ≤ reserves[n] (totalReserves)
                                if (reduceAmount > totalReserves) {
                                    return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION);
                                }
                        
                                /////////////////////////
                                // EFFECTS & INTERACTIONS
                                // (No safe failures beyond this point)
                        
                                totalReservesNew = totalReserves - reduceAmount;
                                // We checked reduceAmount <= totalReserves above, so this should never revert.
                                require(totalReservesNew <= totalReserves, "reduce reserves unexpected underflow");
                        
                                // Store reserves[n+1] = reserves[n] - reduceAmount
                                totalReserves = totalReservesNew;
                        
                                // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
                                doTransferOut(admin, reduceAmount);
                        
                                emit ReservesReduced(admin, reduceAmount, totalReservesNew);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /**
                             * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
                             * @dev Admin function to accrue interest and update the interest rate model
                             * @param newInterestRateModel the new interest rate model to use
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) {
                                uint error = accrueInterest();
                                if (error != uint(Error.NO_ERROR)) {
                                    // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted increase of interest rate model failed
                                    return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED);
                                }
                                // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
                                return _setInterestRateModelFresh(newInterestRateModel);
                            }
                        
                            /**
                             * @notice updates the interest rate model (*requires fresh interest accrual)
                             * @dev Admin function to update the interest rate model
                             * @param newInterestRateModel the new interest rate model to use
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) {
                        
                                // Used to store old model for use in the event that is emitted on success
                                InterestRateModel oldInterestRateModel;
                        
                                // Check caller is admin
                                if (msg.sender != admin) {
                                    return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK);
                                }
                        
                                // We fail gracefully unless market's block number equals current block number
                                if (accrualBlockNumber != getBlockNumber()) {
                                    return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK);
                                }
                        
                                // Track the market's current interest rate model
                                oldInterestRateModel = interestRateModel;
                        
                                // Ensure invoke newInterestRateModel.isInterestRateModel() returns true
                                require(newInterestRateModel.isInterestRateModel(), "marker method returned false");
                        
                                // Set the interest rate model to newInterestRateModel
                                interestRateModel = newInterestRateModel;
                        
                                // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
                                emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);
                        
                                return uint(Error.NO_ERROR);
                            }
                        
                            /*** Safe Token ***/
                        
                            /**
                             * @notice Gets balance of this contract in terms of the underlying
                             * @dev This excludes the value of the current message, if any
                             * @return The quantity of underlying owned by this contract
                             */
                            function getCashPrior() internal view returns (uint);
                        
                            /**
                             * @dev Checks whether or not there is sufficient allowance for this contract to move amount from `from` and
                             *      whether or not `from` has a balance of at least `amount`. Does NOT do a transfer.
                             */
                            function checkTransferIn(address from, uint amount) internal view returns (Error);
                        
                            /**
                             * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
                             *  If caller has not called `checkTransferIn`, this may revert due to insufficient balance or insufficient allowance.
                             *  If caller has called `checkTransferIn` successfully, this should not revert in normal conditions.
                             */
                            function doTransferIn(address from, uint amount) internal returns (uint);
                        
                            /**
                             * @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting.
                             *  If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
                             *  If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
                             */
                            function doTransferOut(address payable to, uint amount) internal;
                        
                        
                            /*** Reentrancy Guard ***/
                        
                            /**
                             * @dev Prevents a contract from calling itself, directly or indirectly.
                             */
                            modifier nonReentrant() {
                                require(_notEntered, "re-entered");
                                _notEntered = false;
                                _;
                                _notEntered = true; // get a gas-refund post-Istanbul
                            }
                        }
                        
                        // File: contracts/CErc20.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        /**
                         * @title Compound's CErc20 Contract
                         * @notice CTokens which wrap an EIP-20 underlying
                         * @author Compound
                         */
                        contract CErc20 is CToken, CErc20Interface {
                            /**
                             * @notice Initialize the new money market
                             * @param underlying_ The address of the underlying asset
                             * @param comptroller_ The address of the Comptroller
                             * @param interestRateModel_ The address of the interest rate model
                             * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
                             * @param name_ ERC-20 name of this token
                             * @param symbol_ ERC-20 symbol of this token
                             * @param decimals_ ERC-20 decimal precision of this token
                             */
                            function initialize(address underlying_,
                                                ComptrollerInterface comptroller_,
                                                InterestRateModel interestRateModel_,
                                                uint initialExchangeRateMantissa_,
                                                string memory name_,
                                                string memory symbol_,
                                                uint8 decimals_) public {
                                // CToken initialize does the bulk of the work
                                super.initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);
                        
                                // Set underlying and sanity check it
                                underlying = underlying_;
                                EIP20Interface(underlying).totalSupply();
                            }
                        
                            /*** User Interface ***/
                        
                            /**
                             * @notice Sender supplies assets into the market and receives cTokens in exchange
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param mintAmount The amount of the underlying asset to supply
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function mint(uint mintAmount) external returns (uint) {
                                (uint err,) = mintInternal(mintAmount);
                                return err;
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for the underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemTokens The number of cTokens to redeem into underlying
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeem(uint redeemTokens) external returns (uint) {
                                return redeemInternal(redeemTokens);
                            }
                        
                            /**
                             * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
                             * @dev Accrues interest whether or not the operation succeeds, unless reverted
                             * @param redeemAmount The amount of underlying to redeem
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function redeemUnderlying(uint redeemAmount) external returns (uint) {
                                return redeemUnderlyingInternal(redeemAmount);
                            }
                        
                            /**
                              * @notice Sender borrows assets from the protocol to their own address
                              * @param borrowAmount The amount of the underlying asset to borrow
                              * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                              */
                            function borrow(uint borrowAmount) external returns (uint) {
                                return borrowInternal(borrowAmount);
                            }
                        
                            /**
                             * @notice Sender repays their own borrow
                             * @param repayAmount The amount to repay
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function repayBorrow(uint repayAmount) external returns (uint) {
                                (uint err,) = repayBorrowInternal(repayAmount);
                                return err;
                            }
                        
                            /**
                             * @notice Sender repays a borrow belonging to borrower
                             * @param borrower the account with the debt being payed off
                             * @param repayAmount The amount to repay
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint) {
                                (uint err,) = repayBorrowBehalfInternal(borrower, repayAmount);
                                return err;
                            }
                        
                            /**
                             * @notice The sender liquidates the borrowers collateral.
                             *  The collateral seized is transferred to the liquidator.
                             * @param borrower The borrower of this cToken to be liquidated
                             * @param repayAmount The amount of the underlying borrowed asset to repay
                             * @param cTokenCollateral The market in which to seize collateral from the borrower
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint) {
                                (uint err,) = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral);
                                return err;
                            }
                        
                            /**
                             * @notice The sender adds to reserves.
                             * @param addAmount The amount fo underlying token to add as reserves
                             * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
                             */
                            function _addReserves(uint addAmount) external returns (uint) {
                                return _addReservesInternal(addAmount);
                            }
                        
                            /*** Safe Token ***/
                        
                            /**
                             * @notice Gets balance of this contract in terms of the underlying
                             * @dev This excludes the value of the current message, if any
                             * @return The quantity of underlying tokens owned by this contract
                             */
                            function getCashPrior() internal view returns (uint) {
                                EIP20Interface token = EIP20Interface(underlying);
                                return token.balanceOf(address(this));
                            }
                        
                            /**
                             * @dev Checks whether or not there is sufficient allowance for this contract to move amount from `from` and
                             *      whether or not `from` has a balance of at least `amount`. Does NOT do a transfer.
                             */
                            function checkTransferIn(address from, uint amount) internal view returns (Error) {
                                EIP20Interface token = EIP20Interface(underlying);
                        
                                if (token.allowance(from, address(this)) < amount) {
                                    return Error.TOKEN_INSUFFICIENT_ALLOWANCE;
                                }
                        
                                if (token.balanceOf(from) < amount) {
                                    return Error.TOKEN_INSUFFICIENT_BALANCE;
                                }
                        
                                return Error.NO_ERROR;
                            }
                        
                            /**
                             * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` reverts in that case.
                             *      If caller has not called `checkTransferIn`, this may revert due to insufficient balance or insufficient
                             *      allowance. If caller has called `checkTransferIn` prior to this call, and it returned Error.NO_ERROR,
                             *      this should not revert in normal conditions. This function returns the actual amount received,
                             *      with may be less than `amount` if there is a fee attached with the transfer.
                             *
                             *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
                             *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
                             */
                            function doTransferIn(address from, uint amount) internal returns (uint) {
                                EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
                                uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this));
                                token.transferFrom(from, address(this), amount);
                        
                                bool success;
                                assembly {
                                    switch returndatasize()
                                        case 0 {                       // This is a non-standard ERC-20
                                            success := not(0)          // set success to true
                                        }
                                        case 32 {                      // This is a compliant ERC-20
                                            returndatacopy(0, 0, 32)
                                            success := mload(0)        // Set `success = returndata` of external call
                                        }
                                        default {                      // This is an excessively non-compliant ERC-20, revert.
                                            revert(0, 0)
                                        }
                                }
                                require(success, "TOKEN_TRANSFER_IN_FAILED");
                        
                                // Calculate the amount that was *actually* transferred
                                uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this));
                                require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
                                return balanceAfter - balanceBefore;   // underflow already checked above, just subtract
                            }
                        
                            /**
                             * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
                             *      error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
                             *      insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
                             *      it is >= amount, this should not revert in normal conditions.
                             *
                             *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
                             *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
                             */
                            function doTransferOut(address payable to, uint amount) internal {
                                EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
                                token.transfer(to, amount);
                        
                                bool success;
                                assembly {
                                    switch returndatasize()
                                        case 0 {                      // This is a non-standard ERC-20
                                            success := not(0)          // set success to true
                                        }
                                        case 32 {                     // This is a complaint ERC-20
                                            returndatacopy(0, 0, 32)
                                            success := mload(0)        // Set `success = returndata` of external call
                                        }
                                        default {                     // This is an excessively non-compliant ERC-20, revert.
                                            revert(0, 0)
                                        }
                                }
                                require(success, "TOKEN_TRANSFER_OUT_FAILED");
                            }
                        }
                        
                        // File: contracts/CErc20Delegate.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        /**
                         * @title Compound's CErc20Delegate Contract
                         * @notice CTokens which wrap an EIP-20 underlying and are delegated to
                         * @author Compound
                         */
                        contract CErc20Delegate is CErc20, CDelegateInterface {
                            /**
                             * @notice Construct an empty delegate
                             */
                            constructor() public {}
                        
                            /**
                             * @notice Called by the delegator on a delegate to initialize it for duty
                             * @param data The encoded bytes data for any initialization
                             */
                            function _becomeImplementation(bytes memory data) public {
                                // Shh -- currently unused
                                data;
                        
                                // Shh -- we don't ever want this hook to be marked pure
                                if (false) {
                                    implementation = address(0);
                                }
                        
                                require(msg.sender == admin, "only the admin may call _becomeImplementation");
                            }
                        
                            /**
                             * @notice Called by the delegator on a delegate to forfeit its responsibility
                             */
                            function _resignImplementation() public {
                                // Shh -- we don't ever want this hook to be marked pure
                                if (false) {
                                    implementation = address(0);
                                }
                        
                                require(msg.sender == admin, "only the admin may call _resignImplementation");
                            }
                        }
                        
                        // File: contracts/CDaiDelegate.sol
                        
                        pragma solidity ^0.5.12;
                        
                        
                        /**
                         * @title Compound's CDai Contract
                         * @notice CToken which wraps Multi-Collateral DAI
                         * @author Compound
                         */
                        contract CDaiDelegate is CErc20Delegate {
                            /**
                             * @notice DAI adapter address
                             */
                            address public daiJoinAddress;
                        
                            /**
                             * @notice DAI Savings Rate (DSR) pot address
                             */
                            address public potAddress;
                        
                            /**
                             * @notice DAI vat address
                             */
                            address public vatAddress;
                        
                            /**
                             * @notice Delegate interface to become the implementation
                             * @param data The encoded arguments for becoming
                             */
                            function _becomeImplementation(bytes memory data) public {
                                require(msg.sender == admin, "only the admin may initialize the implementation");
                        
                                (address daiJoinAddress_, address potAddress_) = abi.decode(data, (address, address));
                                return _becomeImplementation(daiJoinAddress_, potAddress_);
                            }
                        
                            /**
                             * @notice Explicit interface to become the implementation
                             * @param daiJoinAddress_ DAI adapter address
                             * @param potAddress_ DAI Savings Rate (DSR) pot address
                             */
                            function _becomeImplementation(address daiJoinAddress_, address potAddress_) internal {
                                // Get dai and vat and sanity check the underlying
                                DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress_);
                                PotLike pot = PotLike(potAddress_);
                                GemLike dai = daiJoin.dai();
                                VatLike vat = daiJoin.vat();
                                require(address(dai) == underlying, "DAI must be the same as underlying");
                        
                                // Remember the relevant addresses
                                daiJoinAddress = daiJoinAddress_;
                                potAddress = potAddress_;
                                vatAddress = address(vat);
                        
                                // Approve moving our DAI into the vat through daiJoin
                                dai.approve(daiJoinAddress, uint(-1));
                        
                                // Approve the pot to transfer our funds within the vat
                                vat.hope(potAddress);
                                vat.hope(daiJoinAddress);
                        
                                // Accumulate DSR interest -- must do this in order to doTransferIn
                                pot.drip();
                        
                                // Transfer all cash in (doTransferIn does this regardless of amount)
                                doTransferIn(address(this), 0);
                            }
                        
                            /**
                             * @notice Delegate interface to resign the implementation
                             */
                            function _resignImplementation() public {
                                require(msg.sender == admin, "only the admin may abandon the implementation");
                        
                                // Transfer all cash out of the DSR - note that this relies on self-transfer
                                DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
                                PotLike pot = PotLike(potAddress);
                                VatLike vat = VatLike(vatAddress);
                        
                                // Accumulate interest
                                pot.drip();
                        
                                // Calculate the total amount in the pot, and move it out
                                uint pie = pot.pie(address(this));
                                pot.exit(pie);
                        
                                // Checks the actual balance of DAI in the vat after the pot exit
                                uint bal = vat.dai(address(this));
                        
                                // Remove our whole balance
                                daiJoin.exit(address(this), bal / RAY);
                            }
                        
                            /*** CToken Overrides ***/
                        
                            /**
                              * @notice Accrues DSR then applies accrued interest to total borrows and reserves
                              * @dev This calculates interest accrued from the last checkpointed block
                              *      up to the current block and writes new checkpoint to storage.
                              */
                            function accrueInterest() public returns (uint) {
                                // Accumulate DSR interest
                                PotLike(potAddress).drip();
                        
                                // Accumulate CToken interest
                                return super.accrueInterest();
                            }
                        
                            /*** Safe Token ***/
                        
                            /**
                             * @notice Gets balance of this contract in terms of the underlying
                             * @dev This excludes the value of the current message, if any
                             * @return The quantity of underlying tokens owned by this contract
                             */
                            function getCashPrior() internal view returns (uint) {
                                PotLike pot = PotLike(potAddress);
                                uint pie = pot.pie(address(this));
                                return mul(pot.chi(), pie) / RAY;
                            }
                        
                            /**
                             * @notice Transfer the underlying to this contract and sweep into DSR pot
                             * @param from Address to transfer funds from
                             * @param amount Amount of underlying to transfer
                             * @return The actual amount that is transferred
                             */
                            function doTransferIn(address from, uint amount) internal returns (uint) {
                                // Perform the EIP-20 transfer in
                                EIP20Interface token = EIP20Interface(underlying);
                                require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return");
                        
                                DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
                                GemLike dai = GemLike(underlying);
                                PotLike pot = PotLike(potAddress);
                                VatLike vat = VatLike(vatAddress);
                        
                                // Convert all our DAI to internal DAI in the vat
                                daiJoin.join(address(this), dai.balanceOf(address(this)));
                        
                                // Checks the actual balance of DAI in the vat after the join
                                uint bal = vat.dai(address(this));
                        
                                // Calculate the percentage increase to th pot for the entire vat, and move it in
                                // Note: We may leave a tiny bit of DAI in the vat...but we do the whole thing every time
                                uint pie = bal / pot.chi();
                                pot.join(pie);
                        
                                return amount;
                            }
                        
                            /**
                             * @notice Transfer the underlying from this contract, after sweeping out of DSR pot
                             * @param to Address to transfer funds to
                             * @param amount Amount of underlying to transfer
                             */
                            function doTransferOut(address payable to, uint amount) internal {
                                DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
                                PotLike pot = PotLike(potAddress);
                        
                                // Calculate the percentage decrease from the pot, and move that much out
                                // Note: Use a slightly larger pie size to ensure that we get at least amount in the vat
                                uint pie = add(mul(amount, RAY) / pot.chi(), 1);
                                pot.exit(pie);
                        
                                daiJoin.exit(to, amount);
                            }
                        
                            /*** Maker Internals ***/
                        
                            uint256 constant RAY = 10 ** 27;
                        
                            function add(uint x, uint y) internal pure returns (uint z) {
                                require((z = x + y) >= x, "add-overflow");
                            }
                        
                            function mul(uint x, uint y) internal pure returns (uint z) {
                                require(y == 0 || (z = x * y) / y == x, "mul-overflow");
                            }
                        }
                        
                        /*** Maker Interfaces ***/
                        
                        contract PotLike {
                            function chi() public view returns (uint);
                            function dsr() public view returns (uint);
                            function rho() public view returns (uint);
                            function pie(address) public view returns (uint);
                            function drip() public returns (uint);
                            function join(uint) public;
                            function exit(uint) public;
                        }
                        
                        contract GemLike {
                            function approve(address, uint) public;
                            function balanceOf(address) public view returns (uint);
                            function transfer(address, uint) public;
                            function transferFrom(address, address, uint) public;
                            function deposit() public payable;
                            function withdraw(uint) public;
                        }
                        
                        contract VatLike {
                            function can(address, address) public view returns (uint);
                            function ilks(bytes32) public view returns (uint, uint, uint, uint, uint);
                            function dai(address) public view returns (uint);
                            function urns(bytes32, address) public view returns (uint, uint);
                            function frob(bytes32, address, address, address, int, int) public;
                            function hope(address) public;
                            function move(address, address, uint) public;
                        }
                        
                        contract DaiJoinLike {
                            function vat() public returns (VatLike);
                            function dai() public returns (GemLike);
                            function join(address, uint) public payable;
                            function exit(address, uint) public;
                        }
                        

                        File 6 of 6: DAIInterestRateModelV3
                        pragma solidity ^0.5.16;
                        
                        
                        /**
                          * @title Compound's InterestRateModel Interface
                          * @author Compound
                          */
                        contract InterestRateModel {
                            /// @notice Indicator that this is an InterestRateModel contract (for inspection)
                            bool public constant isInterestRateModel = true;
                        
                            /**
                              * @notice Calculates the current borrow interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @return The borrow rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);
                        
                            /**
                              * @notice Calculates the current supply interest rate per block
                              * @param cash The total amount of cash the market has
                              * @param borrows The total amount of borrows the market has outstanding
                              * @param reserves The total amnount of reserves the market has
                              * @param reserveFactorMantissa The current reserve factor the market has
                              * @return The supply rate per block (as a percentage, and scaled by 1e18)
                              */
                            function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint);
                        }
                        
                        
                        // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
                        // Subject to the MIT license.
                        
                        /**
                         * @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 addition of two unsigned integers, reverting with custom message on overflow.
                             *
                             * Counterpart to Solidity's `+` operator.
                             *
                             * Requirements:
                             * - Addition cannot overflow.
                             */
                            function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a, errorMessage);
                        
                                return c;
                            }
                        
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot underflow.
                             */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                return sub(a, b, "SafeMath: subtraction underflow");
                            }
                        
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot underflow.
                             */
                            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 multiplication of two unsigned integers, reverting on overflow.
                             *
                             * Counterpart to Solidity's `*` operator.
                             *
                             * Requirements:
                             * - Multiplication cannot overflow.
                             */
                            function mul(uint256 a, uint256 b, string memory errorMessage) 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, errorMessage);
                        
                                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) {
                                // Solidity only automatically asserts when dividing by 0
                                require(b > 0, errorMessage);
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                        
                                return c;
                            }
                        
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                return mod(a, b, "SafeMath: modulo by zero");
                            }
                        
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts with custom message when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b != 0, errorMessage);
                                return a % b;
                            }
                        }
                        
                        
                        /**
                          * @title Compound's JumpRateModel Contract V2
                          * @author Compound (modified by Dharma Labs)
                          * @notice Version 2 modifies Version 1 by enabling updateable parameters.
                          */
                        contract JumpRateModelV2 is InterestRateModel {
                            using SafeMath for uint;
                        
                            event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink);
                        
                            /**
                             * @notice The address of the owner, i.e. the Timelock contract, which can update parameters directly
                             */
                            address public owner;
                        
                            /**
                             * @notice The approximate number of blocks per year that is assumed by the interest rate model
                             */
                            uint public constant blocksPerYear = 2102400;
                        
                            /**
                             * @notice The multiplier of utilization rate that gives the slope of the interest rate
                             */
                            uint public multiplierPerBlock;
                        
                            /**
                             * @notice The base interest rate which is the y-intercept when utilization rate is 0
                             */
                            uint public baseRatePerBlock;
                        
                            /**
                             * @notice The multiplierPerBlock after hitting a specified utilization point
                             */
                            uint public jumpMultiplierPerBlock;
                        
                            /**
                             * @notice The utilization point at which the jump multiplier is applied
                             */
                            uint public kink;
                        
                            /**
                             * @notice Construct an interest rate model
                             * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18)
                             * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18)
                             * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
                             * @param kink_ The utilization point at which the jump multiplier is applied
                             * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly)
                             */
                            constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_, address owner_) public {
                                owner = owner_;
                        
                                updateJumpRateModelInternal(baseRatePerYear,  multiplierPerYear, jumpMultiplierPerYear, kink_);
                            }
                        
                            /**
                             * @notice Update the parameters of the interest rate model (only callable by owner, i.e. Timelock)
                             * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18)
                             * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18)
                             * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
                             * @param kink_ The utilization point at which the jump multiplier is applied
                             */
                            function updateJumpRateModel(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) external {
                                require(msg.sender == owner, "only the owner may call this function.");
                        
                                updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_);
                            }
                        
                            /**
                             * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`
                             * @param cash The amount of cash in the market
                             * @param borrows The amount of borrows in the market
                             * @param reserves The amount of reserves in the market (currently unused)
                             * @return The utilization rate as a mantissa between [0, 1e18]
                             */
                            function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) {
                                // Utilization rate is 0 when there are no borrows
                                if (borrows == 0) {
                                    return 0;
                                }
                        
                                return borrows.mul(1e18).div(cash.add(borrows).sub(reserves));
                            }
                        
                            /**
                             * @notice Calculates the current borrow rate per block, with the error code expected by the market
                             * @param cash The amount of cash in the market
                             * @param borrows The amount of borrows in the market
                             * @param reserves The amount of reserves in the market
                             * @return The borrow rate percentage per block as a mantissa (scaled by 1e18)
                             */
                            function getBorrowRate(uint cash, uint borrows, uint reserves) public view returns (uint) {
                                uint util = utilizationRate(cash, borrows, reserves);
                        
                                if (util <= kink) {
                                    return util.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock);
                                } else {
                                    uint normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock);
                                    uint excessUtil = util.sub(kink);
                                    return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate);
                                }
                            }
                        
                            /**
                             * @notice Calculates the current supply rate per block
                             * @param cash The amount of cash in the market
                             * @param borrows The amount of borrows in the market
                             * @param reserves The amount of reserves in the market
                             * @param reserveFactorMantissa The current reserve factor for the market
                             * @return The supply rate percentage per block as a mantissa (scaled by 1e18)
                             */
                            function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) {
                                uint oneMinusReserveFactor = uint(1e18).sub(reserveFactorMantissa);
                                uint borrowRate = getBorrowRate(cash, borrows, reserves);
                                uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18);
                                return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18);
                            }
                        
                            /**
                             * @notice Internal function to update the parameters of the interest rate model
                             * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18)
                             * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18)
                             * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
                             * @param kink_ The utilization point at which the jump multiplier is applied
                             */
                            function updateJumpRateModelInternal(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) internal {
                                baseRatePerBlock = baseRatePerYear.div(blocksPerYear);
                                multiplierPerBlock = (multiplierPerYear.mul(1e18)).div(blocksPerYear.mul(kink_));
                                jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear);
                                kink = kink_;
                        
                                emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);
                            }
                        }
                        
                        
                        /**
                          * @title Compound's DAIInterestRateModel Contract (version 3)
                          * @author Compound (modified by Dharma Labs)
                          * @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper.
                          * Version 3 modifies the interest rate model in Version 2 by increasing the initial "gap" or slope of
                          * the model prior to the "kink" from 2% to 4%, and enabling updateable parameters.
                          */
                        contract DAIInterestRateModelV3 is JumpRateModelV2 {
                            using SafeMath for uint;
                        
                            /**
                             * @notice The additional margin per block separating the base borrow rate from the roof.
                             */
                            uint public gapPerBlock;
                        
                            /**
                             * @notice The assumed (1 - reserve factor) used to calculate the minimum borrow rate (reserve factor = 0.05)
                             */
                            uint public constant assumedOneMinusReserveFactorMantissa = 0.95e18;
                        
                            PotLike pot;
                            JugLike jug;
                        
                            /**
                             * @notice Construct an interest rate model
                             * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
                             * @param kink_ The utilization point at which the jump multiplier is applied
                             * @param pot_ The address of the Dai pot (where DSR is earned)
                             * @param jug_ The address of the Dai jug (where SF is kept)
                             * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly)
                             */
                            constructor(uint jumpMultiplierPerYear, uint kink_, address pot_, address jug_, address owner_) JumpRateModelV2(0, 0, jumpMultiplierPerYear, kink_, owner_) public {
                                gapPerBlock = 4e16 / blocksPerYear;
                                pot = PotLike(pot_);
                                jug = JugLike(jug_);
                                poke();
                            }
                        
                            /**
                             * @notice External function to update the parameters of the interest rate model
                             * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18). For DAI, this is calculated from DSR and SF. Input not used.
                             * @param gapPerYear The Additional margin per year separating the base borrow rate from the roof. (scaled by 1e18)
                             * @param jumpMultiplierPerYear The jumpMultiplierPerYear after hitting a specified utilization point
                             * @param kink_ The utilization point at which the jump multiplier is applied
                             */
                            function updateJumpRateModel(uint baseRatePerYear, uint gapPerYear, uint jumpMultiplierPerYear, uint kink_) external {
                                require(msg.sender == owner, "only the owner may call this function.");
                                gapPerBlock = gapPerYear / blocksPerYear;
                                updateJumpRateModelInternal(0, 0, jumpMultiplierPerYear, kink_);
                                poke();
                            }
                        
                            /**
                             * @notice Calculates the current supply interest rate per block including the Dai savings rate
                             * @param cash The total amount of cash the market has
                             * @param borrows The total amount of borrows the market has outstanding
                             * @param reserves The total amnount of reserves the market has
                             * @param reserveFactorMantissa The current reserve factor the market has
                             * @return The supply rate per block (as a percentage, and scaled by 1e18)
                             */
                            function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) {
                                uint protocolRate = super.getSupplyRate(cash, borrows, reserves, reserveFactorMantissa);
                        
                                uint underlying = cash.add(borrows).sub(reserves);
                                if (underlying == 0) {
                                    return protocolRate;
                                } else {
                                    uint cashRate = cash.mul(dsrPerBlock()).div(underlying);
                                    return cashRate.add(protocolRate);
                                }
                            }
                        
                            /**
                             * @notice Calculates the Dai savings rate per block
                             * @return The Dai savings rate per block (as a percentage, and scaled by 1e18)
                             */
                            function dsrPerBlock() public view returns (uint) {
                                return pot
                                    .dsr().sub(1e27)  // scaled 1e27 aka RAY, and includes an extra "ONE" before subraction
                                    .div(1e9) // descale to 1e18
                                    .mul(15); // 15 seconds per block
                            }
                        
                            /**
                             * @notice Resets the baseRate and multiplier per block based on the stability fee and Dai savings rate
                             */
                            function poke() public {
                                (uint duty, ) = jug.ilks("ETH-A");
                                uint stabilityFeePerBlock = duty.add(jug.base()).sub(1e27).mul(1e18).div(1e27).mul(15);
                        
                                // We ensure the minimum borrow rate >= DSR / (1 - reserve factor)
                                baseRatePerBlock = dsrPerBlock().mul(1e18).div(assumedOneMinusReserveFactorMantissa);
                        
                                // The roof borrow rate is max(base rate, stability fee) + gap, from which we derive the slope
                                if (baseRatePerBlock < stabilityFeePerBlock) {
                                    multiplierPerBlock = stabilityFeePerBlock.sub(baseRatePerBlock).add(gapPerBlock).mul(1e18).div(kink);
                                } else {
                                    multiplierPerBlock = gapPerBlock.mul(1e18).div(kink);
                                }
                        
                                emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);
                            }
                        }
                        
                        
                        /*** Maker Interfaces ***/
                        
                        contract PotLike {
                            function chi() external view returns (uint);
                            function dsr() external view returns (uint);
                            function rho() external view returns (uint);
                            function pie(address) external view returns (uint);
                            function drip() external returns (uint);
                            function join(uint) external;
                            function exit(uint) external;
                        }
                        
                        contract JugLike {
                            // --- Data ---
                            struct Ilk {
                                uint256 duty;
                                uint256  rho;
                            }
                        
                           mapping (bytes32 => Ilk) public ilks;
                           uint256 public base;
                        }