ETH Price: $2,718.91 (+1.75%)

Contract

0x836b557Cf9eF29fcF49C776841191782df34e4e5
Transaction Hash
Method
Block
From
To
Transfer Ownersh...95983352024-02-01 17:35:03382 days ago1706808903IN
0x836b557C...2df34e4e5
0 ETH0.000216757.5
Owner Add Market95983292024-02-01 17:34:53382 days ago1706808893IN
0x836b557C...2df34e4e5
0 ETH0.001853677.5
Owner Set Auto T...95983272024-02-01 17:34:53382 days ago1706808893IN
0x836b557C...2df34e4e5
0 ETH0.000395337.5
Owner Set Global...95983242024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983232024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983222024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983212024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983202024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983192024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983182024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983172024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5
Owner Set Global...95983162024-02-01 17:34:43382 days ago1706808883IN
0x836b557C...2df34e4e5
0 ETH0.000395647.5

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DolomiteMargin

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 50 : DolomiteMargin.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Admin } from "./Admin.sol";
import { Getters } from "./Getters.sol";
import { Operation } from "./Operation.sol";
import { Permission } from "./Permission.sol";

import { AdminImpl } from "./impl/AdminImpl.sol";

import { IDolomiteMargin } from "./interfaces/IDolomiteMargin.sol";
import { IOracleSentinel } from "./interfaces/IOracleSentinel.sol";

import { Decimal } from "./lib/Decimal.sol";
import { Monetary } from "./lib/Monetary.sol";
import { Storage } from "./lib/Storage.sol";


/**
 * @title DolomiteMargin
 * @author dYdX
 *
 * Main contract that inherits from other contracts
 */
contract DolomiteMargin is
    IDolomiteMargin,
    Admin,
    Getters,
    Operation,
    Permission
{
    // ============ Constructor ============

    constructor(
        Storage.RiskLimits memory riskLimits,
        Decimal.D256 memory marginRatio,
        Decimal.D256 memory liquidationSpread,
        Decimal.D256 memory earningsRate,
        Monetary.Value memory minBorrowedValue,
        uint256 accountMaxNumberOfMarketsWithBalances,
        IOracleSentinel oracleSentinel,
        uint256 callbackGasLimit
    )
        public
    {
        g_state.riskLimits = riskLimits;
        AdminImpl.ownerSetMarginRatio(g_state, marginRatio);
        AdminImpl.ownerSetLiquidationSpread(g_state, liquidationSpread);
        AdminImpl.ownerSetEarningsRate(g_state, earningsRate);
        AdminImpl.ownerSetMinBorrowedValue(g_state, minBorrowedValue);
        AdminImpl.ownerSetAccountMaxNumberOfMarketsWithBalances(g_state, accountMaxNumberOfMarketsWithBalances);
        AdminImpl.ownerSetOracleSentinel(g_state, oracleSentinel);
        AdminImpl.ownerSetCallbackGasLimit(g_state, callbackGasLimit);
    }
}

File 2 of 50 : IPriceOracle.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Monetary } from "../lib/Monetary.sol";


/**
 * @title IPriceOracle
 * @author dYdX
 *
 * Interface that Price Oracles for DolomiteMargin must implement in order to report prices.
 */
contract IPriceOracle {

    // ============ Constants ============

    uint256 public constant ONE_DOLLAR = 10 ** 36;

    // ============ Public Functions ============

    /**
     * Get the price of a token
     *
     * @param  token  The ERC20 token address of the market
     * @return        The USD price of a base unit of the token, then multiplied by 10^36.
     *                So a USD-stable coin with 18 decimal places would return 10^18.
     *                This is the price of the base unit rather than the price of a "human-readable"
     *                token amount. Every ERC20 may have a different number of decimals.
     */
    function getPrice(
        address token
    )
        public
        view
        returns (Monetary.Price memory);
}

File 3 of 50 : IERC20.sol
pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 4 of 50 : Ownable.sol
pragma solidity ^0.5.0;

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

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

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

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

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

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

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

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

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

File 5 of 50 : SafeMath.sol
pragma solidity ^0.5.0;

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

File 6 of 50 : Math.sol
pragma solidity ^0.5.0;

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

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

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

File 7 of 50 : Context.sol
pragma solidity ^0.5.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

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

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

File 8 of 50 : Types.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";


/**
 * @title Types
 * @author dYdX
 *
 * Library for interacting with the basic structs used in DolomiteMargin
 */
library Types {
    using DolomiteMarginMath for uint256;

    // ============ Permission ============

    struct OperatorArg {
        address operator;
        bool trusted;
    }

    // ============ AssetAmount ============

    enum AssetDenomination {
        Wei, // the amount is denominated in wei
        Par  // the amount is denominated in par
    }

    enum AssetReference {
        Delta, // the amount is given as a delta from the current value
        Target // the amount is given as an exact number to end up at
    }

    struct AssetAmount {
        bool sign; // true if positive
        AssetDenomination denomination;
        AssetReference ref;
        uint256 value;
    }

    // ============ Par (Principal Amount) ============

    // Total borrow and supply values for a market
    struct TotalPar {
        uint128 borrow;
        uint128 supply;
    }

    // Individual principal amount for an account
    struct Par {
        bool sign; // true if positive
        uint128 value;
    }

    function zeroPar()
        internal
        pure
        returns (Par memory)
    {
        return Par({
            sign: false,
            value: 0
        });
    }

    function sub(
        Par memory a,
        Par memory b
    )
        internal
        pure
        returns (Par memory)
    {
        return add(a, negative(b));
    }

    function add(
        Par memory a,
        Par memory b
    )
        internal
        pure
        returns (Par memory)
    {
        Par memory result;
        if (a.sign == b.sign) {
            result.sign = a.sign;
            result.value = SafeMath.add(a.value, b.value).to128();
        } else {
            if (a.value >= b.value) {
                result.sign = a.sign;
                result.value = SafeMath.sub(a.value, b.value).to128();
            } else {
                result.sign = b.sign;
                result.value = SafeMath.sub(b.value, a.value).to128();
            }
        }
        return result;
    }

    function equals(
        Par memory a,
        Par memory b
    )
        internal
        pure
        returns (bool)
    {
        if (a.value == b.value) {
            if (a.value == 0) {
                return true;
            }
            return a.sign == b.sign;
        }
        return false;
    }

    function negative(
        Par memory a
    )
        internal
        pure
        returns (Par memory)
    {
        return Par({
            sign: !a.sign,
            value: a.value
        });
    }

    function isNegative(
        Par memory a
    )
        internal
        pure
        returns (bool)
    {
        return !a.sign && a.value != 0;
    }

    function isPositive(
        Par memory a
    )
        internal
        pure
        returns (bool)
    {
        return a.sign && a.value != 0;
    }

    function isZero(
        Par memory a
    )
        internal
        pure
        returns (bool)
    {
        return a.value == 0;
    }

    function isLessThanZero(
        Par memory a
    )
        internal
        pure
        returns (bool)
    {
        return a.value != 0 && !a.sign;
    }

    function isGreaterThanOrEqualToZero(
        Par memory a
    )
        internal
        pure
        returns (bool)
    {
        return isZero(a) || a.sign;
    }

    // ============ Wei (Token Amount) ============

    struct TotalWei {
        uint128 borrow;
        uint128 supply;
    }

    // Individual token amount for an account
    struct Wei {
        bool sign; // true if positive
        uint256 value;
    }

    function zeroWei()
        internal
        pure
        returns (Wei memory)
    {
        return Wei({
            sign: false,
            value: 0
        });
    }

    function sub(
        Wei memory a,
        Wei memory b
    )
        internal
        pure
        returns (Wei memory)
    {
        return add(a, negative(b));
    }

    function add(
        Wei memory a,
        Wei memory b
    )
        internal
        pure
        returns (Wei memory)
    {
        Wei memory result;
        if (a.sign == b.sign) {
            result.sign = a.sign;
            result.value = SafeMath.add(a.value, b.value);
        } else {
            if (a.value >= b.value) {
                result.sign = a.sign;
                result.value = SafeMath.sub(a.value, b.value);
            } else {
                result.sign = b.sign;
                result.value = SafeMath.sub(b.value, a.value);
            }
        }
        return result;
    }

    function equals(
        Wei memory a,
        Wei memory b
    )
        internal
        pure
        returns (bool)
    {
        if (a.value == b.value) {
            if (a.value == 0) {
                return true;
            }
            return a.sign == b.sign;
        }
        return false;
    }

    function negative(
        Wei memory a
    )
        internal
        pure
        returns (Wei memory)
    {
        return Wei({
            sign: !a.sign,
            value: a.value
        });
    }

    function isNegative(
        Wei memory a
    )
        internal
        pure
        returns (bool)
    {
        return !a.sign && a.value != 0;
    }

    function isPositive(
        Wei memory a
    )
        internal
        pure
        returns (bool)
    {
        return a.sign && a.value != 0;
    }

    function isZero(
        Wei memory a
    )
        internal
        pure
        returns (bool)
    {
        return a.value == 0;
    }
}

File 9 of 50 : Token.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { IERC20Detailed } from "../interfaces/IERC20Detailed.sol";


/**
 * @title Token
 * @author dYdX
 *
 * This library contains basic functions for interacting with ERC20 tokens. Modified to work with
 * tokens that don't adhere strictly to the ERC20 standard (for example tokens that don't return a
 * boolean value on success).
 */
library Token {

    // ============ Library Functions ============

    function transfer(
        address token,
        address to,
        uint256 amount
    )
        internal
    {
        if (amount == 0 || to == address(this)) {
            return;
        }

        _callOptionalReturn(
            token,
            abi.encodeWithSelector(IERC20Detailed(token).transfer.selector, to, amount),
            "Token: transfer failed"
        );
    }

    function transferFrom(
        address token,
        address from,
        address to,
        uint256 amount
    )
        internal
    {
        if (amount == 0 || to == from) {
            return;
        }

        // solium-disable arg-overflow
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(IERC20Detailed(token).transferFrom.selector, from, to, amount),
            "Token: transferFrom failed"
        );
        // solium-enable arg-overflow
    }

    // ============ Private Functions ============

    function _callOptionalReturn(address token, bytes memory data, string memory error) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        // 1. The target address is checked to contain contract code. Not needed since tokens are manually added
        // 2. The call itself is made, and success asserted
        // 3. The return value is decoded, which in turn checks the size of the returned data.

        // solium-disable-next-line security/no-low-level-calls
        (bool success, bytes memory returnData) = token.call(data);
        require(success, error);

        if (returnData.length != 0) {
            // Return data is optional
            require(abi.decode(returnData, (bool)), error);
        }
    }

}

File 10 of 50 : Time.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";


/**
 * @title Time
 * @author dYdX
 *
 * Library for dealing with time, assuming timestamps fit within 32 bits (valid until year 2106)
 */
library Time {

    // ============ Library Functions ============

    function currentTime()
        internal
        view
        returns (uint32)
    {
        return DolomiteMarginMath.to32(block.timestamp);
    }
}

File 11 of 50 : Storage.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";

import { Account } from "./Account.sol";
import { Bits } from "./Bits.sol";
import { Cache } from "./Cache.sol";
import { Decimal } from "./Decimal.sol";
import { Interest } from "./Interest.sol";
import { EnumerableSet } from "./EnumerableSet.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
import { Monetary } from "./Monetary.sol";
import { Require } from "./Require.sol";
import { Time } from "./Time.sol";
import { Token } from "./Token.sol";
import { Types } from "./Types.sol";

import { IAccountRiskOverrideSetter } from "../interfaces/IAccountRiskOverrideSetter.sol";
import { IERC20Detailed } from "../interfaces/IERC20Detailed.sol";
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "../interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";


/**
 * @title Storage
 * @author dYdX
 *
 * Functions for reading, writing, and verifying state in DolomiteMargin
 */
library Storage {
    using Cache for Cache.MarketCache;
    using Storage for Storage.State;
    using DolomiteMarginMath for uint256;
    using Types for Types.Par;
    using Types for Types.Wei;
    using SafeMath for uint256;
    using EnumerableSet for EnumerableSet.Set;

    // ============ Constants ============

    bytes32 private constant FILE = "Storage";

    // ============ Structs ============

    // All information necessary for tracking a market
    struct Market {
        // Contract address of the associated ERC20 token
        address token;

        // Whether additional borrows are allowed for this market
        bool isClosing;

        // Total aggregated supply and borrow amount of the entire market
        Types.TotalPar totalPar;

        // Interest index of the market
        Interest.Index index;

        // Contract address of the price oracle for this market
        IPriceOracle priceOracle;

        // Contract address of the interest setter for this market
        IInterestSetter interestSetter;

        // Multiplier on the marginRatio for this market, IE 5% (0.05 * 1e18). This number increases the market's
        // required collateralization by: reducing the user's supplied value (in terms of dollars) for this market and
        // increasing its borrowed value. This is done through the following operation:
        // `suppliedWei = suppliedWei + (assetValueForThisMarket / (1 + marginPremium))`
        // This number increases the user's borrowed wei by multiplying it by:
        // `borrowedWei = borrowedWei + (assetValueForThisMarket * (1 + marginPremium))`
        Decimal.D256 marginPremium;

        // Multiplier on the liquidationSpread for this market, IE 20% (0.2 * 1e18). This number increases the
        // `liquidationSpread` using the following formula:
        // `liquidationSpread = liquidationSpread * (1 + spreadPremium)`
        // NOTE: This formula is applied up to two times - one for each market whose spreadPremium is greater than 0
        // (when performing a liquidation between two markets)
        Decimal.D256 liquidationSpreadPremium;

        // The maximum amount that can be held by the protocol. This allows the protocol to cap any additional risk
        // that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
        // value to 0 is analogous to having no limit. This value can never be below 0.
        Types.Wei maxSupplyWei;

        // The maximum amount that can be borrowed by the protocol. This allows the protocol to cap any additional risk
        // that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
        // value to 0 is analogous to having no limit. This value can never be greater than 0.
        Types.Wei maxBorrowWei;

        // The percentage of interest paid that is passed along from borrowers to suppliers. Setting this to 0 will
        // default to RiskParams.earningsRate.
        Decimal.D256 earningsRateOverride;
    }

    // The global risk parameters that govern the health and security of the system
    struct RiskParams {
        // Required ratio of over-collateralization
        Decimal.D256 marginRatio;

        // Percentage penalty incurred by liquidated accounts
        Decimal.D256 liquidationSpread;

        // Percentage of the borrower's interest fee that gets passed to the suppliers
        Decimal.D256 earningsRate;

        // The minimum absolute borrow value of an account
        // There must be sufficient incentivize to liquidate undercollateralized accounts
        Monetary.Value minBorrowedValue;

        // The maximum number of markets a user can have a non-zero balance for a given account.
        uint256 accountMaxNumberOfMarketsWithBalances;

        // The oracle sentinel used to disable borrowing/liquidations if the sequencer goes down
        IOracleSentinel oracleSentinel;

        // The gas limit used for making callbacks via `IExternalCallback::onInternalBalanceChange` to smart contract
        // wallets. Setting to 0 will effectively disable callbacks; setting it super large is not desired since it
        // could lead to DOS attacks on the protocol; however, hard coding a max value isn't preferred since some chains
        // can calculate gas usage differently (like ArbGas before Arbitrum rolled out nitro)
        uint256 callbackGasLimit;

        // The default account risk override setter. By default, we ping this for any overrides in risk controls,
        // if the `accountRiskOverrideSetterMap` resolves to 0x0. If this value is set to `0x0` there is no default.
        IAccountRiskOverrideSetter defaultAccountRiskOverrideSetter;

        // Certain addresses are allowed to borrow with different LTV requirements. When an account's risk is overrode,
        // the global risk parameters are ignored and the account's risk parameters are used instead.
        mapping(address => IAccountRiskOverrideSetter) accountRiskOverrideSetterMap;
    }

    // The maximum RiskParam values that can be set
    struct RiskLimits {
        // The highest that the ratio can be for liquidating under-water accounts
        uint64 marginRatioMax;
        // The highest that the liquidation rewards can be when a liquidator liquidates an account
        uint64 liquidationSpreadMax;
        // The highest that the supply APR can be for a market, as a proportion of the borrow rate. Meaning, a rate of
        // 100% (1e18) would give suppliers all of the interest that borrowers are paying. A rate of 90% would give
        // suppliers 90% of the interest that borrowers pay.
        uint64 earningsRateMax;
        // The highest min margin ratio premium that can be applied to a particular market. Meaning, a value of 100%
        // (1e18) would require borrowers to maintain an extra 100% collateral to maintain a healthy margin ratio. This
        // value works by increasing the debt owed and decreasing the supply held for the particular market by this
        // amount, plus 1e18 (since a value of 10% needs to be applied as `decimal.plusOne`)
        uint64 marginPremiumMax;
        // The highest liquidation reward that can be applied to a particular market. This percentage is applied
        // in addition to the liquidation spread in `RiskParams`. Meaning a value of 1e18 is 100%. It is calculated as:
        // `liquidationSpread * Decimal.onePlus(spreadPremium)`
        uint64 liquidationSpreadPremiumMax;
        // The highest that the borrow interest rate can ever be. If the rate returned is ever higher, the rate is
        // capped at this value instead of reverting. The goal is to keep Dolomite operational under all circumstances
        // instead of inadvertently DOS'ing the protocol.
        uint96 interestRateMax;
        // The highest that the minBorrowedValue can be. This is the minimum amount of value that must be borrowed.
        // Typically a value of $100 (100 * 1e18) is more than sufficient.
        uint128 minBorrowedValueMax;
    }

    // The entire storage state of DolomiteMargin
    struct State {
        // number of markets
        uint256 numMarkets;

        // marketId => Market
        mapping (uint256 => Market) markets;

        // token address => marketId
        mapping (address => uint256) tokenToMarketId;

        // owner => account number => Account
        mapping (address => mapping (uint256 => Account.Storage)) accounts;

        // Addresses that can control other users accounts
        mapping (address => mapping (address => uint256)) operators;

        // Addresses that can control all users accounts
        mapping (address => uint256) globalOperators;

        // Addresses of auto traders that can only be called by global operators. IE for expirations
        mapping (address => uint256) specialAutoTraders;

        // mutable risk parameters of the system
        RiskParams riskParams;

        // immutable risk limits of the system
        RiskLimits riskLimits;
    }

    // ============ Functions ============

    function getToken(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (address)
    {
        return state.markets[marketId].token;
    }

    function getTotalPar(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (Types.TotalPar memory)
    {
        return state.markets[marketId].totalPar;
    }

    function getMaxSupplyWei(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (Types.Wei memory)
    {
        return state.markets[marketId].maxSupplyWei;
    }

    function getMaxBorrowWei(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (Types.Wei memory)
    {
        return state.markets[marketId].maxBorrowWei;
    }

    function getIndex(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (Interest.Index memory)
    {
        return state.markets[marketId].index;
    }

    function getNumExcessTokens(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        view
        returns (Types.Wei memory)
    {
        Interest.Index memory index = state.getIndex(marketId);
        Types.TotalPar memory totalPar = state.getTotalPar(marketId);

        address token = state.getToken(marketId);

        Types.Wei memory balanceWei = Types.Wei({
            sign: true,
            value: IERC20Detailed(token).balanceOf(address(this))
        });

        (
            Types.Wei memory supplyWei,
            Types.Wei memory borrowWei
        ) = Interest.totalParToWei(totalPar, index);

        // borrowWei is negative, so subtracting it makes the value more positive
        return balanceWei.sub(borrowWei).sub(supplyWei);
    }

    function getStatus(
        Storage.State storage state,
        Account.Info memory account
    )
        internal
        view
        returns (Account.Status)
    {
        return state.accounts[account.owner][account.number].status;
    }

    function getPar(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId
    )
        internal
        view
        returns (Types.Par memory)
    {
        return state.accounts[account.owner][account.number].balances[marketId];
    }

    function getWei(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId,
        Interest.Index memory index
    )
        internal
        view
        returns (Types.Wei memory)
    {
        Types.Par memory par = state.getPar(account, marketId);

        if (par.isZero()) {
            return Types.zeroWei();
        }

        return Interest.parToWei(par, index);
    }

    function getMarketsWithBalances(
        Storage.State storage state,
        Account.Info memory account
    )
    internal
    view
    returns (uint256[] memory)
    {
        return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.values();
    }

    function getAccountMarketWithBalanceAtIndex(
        Storage.State storage state,
        Account.Info memory account,
        uint256 index
    )
    internal
    view
    returns (uint256)
    {
        return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.getAtIndex(index);
    }

    function getNumberOfMarketsWithBalances(
        Storage.State storage state,
        Account.Info memory account
    )
    internal
    view
    returns (uint256)
    {
        return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.length();
    }

    function getAccountNumberOfMarketsWithDebt(
        Storage.State storage state,
        Account.Info memory account
    )
    internal
    view
    returns (uint256)
    {
        return state.accounts[account.owner][account.number].numberOfMarketsWithDebt;
    }

    function getLiquidationSpreadForAccountAndPair(
        Storage.State storage state,
        Account.Info memory account,
        uint256 heldMarketId,
        uint256 owedMarketId
    )
        internal
        view
        returns (Decimal.D256 memory)
    {
        (, Decimal.D256 memory liquidationSpreadOverride) = getAccountRiskOverride(state, account);
        if (liquidationSpreadOverride.value != 0) {
            return liquidationSpreadOverride;
        }

        uint256 result = state.riskParams.liquidationSpread.value;
        result = Decimal.mul(result, Decimal.onePlus(state.markets[heldMarketId].liquidationSpreadPremium));
        result = Decimal.mul(result, Decimal.onePlus(state.markets[owedMarketId].liquidationSpreadPremium));
        return Decimal.D256({
            value: result
        });
    }

    function fetchNewIndex(
        Storage.State storage state,
        uint256 marketId,
        Interest.Index memory index
    )
        internal
        view
        returns (Interest.Index memory)
    {
        Interest.Rate memory rate = state.fetchInterestRate(marketId, index);

        Decimal.D256 memory earningsRate = state.markets[marketId].earningsRateOverride;
        if (earningsRate.value == 0) {
            // The earnings rate was not override, fall back to the global one
            earningsRate = state.riskParams.earningsRate;
        }

        return Interest.calculateNewIndex(
            index,
            rate,
            state.getTotalPar(marketId),
            earningsRate
        );
    }

    function fetchInterestRate(
        Storage.State storage state,
        uint256 marketId,
        Interest.Index memory index
    )
        internal
        view
        returns (Interest.Rate memory)
    {
        Types.TotalPar memory totalPar = state.getTotalPar(marketId);
        (
            Types.Wei memory supplyWei,
            Types.Wei memory borrowWei
        ) = Interest.totalParToWei(totalPar, index);

        Interest.Rate memory rate = state.markets[marketId].interestSetter.getInterestRate(
            state.getToken(marketId),
            borrowWei.value,
            supplyWei.value
        );

        if (rate.value > state.riskLimits.interestRateMax) {
            // Cap the interest rate at the max instead of reverting. We don't want to DOS the protocol
            rate.value = state.riskLimits.interestRateMax;
        }

        return rate;
    }

    function fetchPrice(
        Storage.State storage state,
        uint256 marketId,
        address token
    )
        internal
        view
        returns (Monetary.Price memory)
    {
        IPriceOracle oracle = IPriceOracle(state.markets[marketId].priceOracle);
        Monetary.Price memory price = oracle.getPrice(token);
        Require.that(
            price.value != 0,
            FILE,
            "Price cannot be zero",
            marketId
        );
        return price;
    }

    // solium-disable-next-line security/no-assign-params
    function getAccountValues(
        Storage.State storage state,
        Account.Info memory account,
        Cache.MarketCache memory cache,
        bool adjustForLiquidity,
        Decimal.D256 memory marginRatioOverride
    )
        internal
        view
        returns (Monetary.Value memory, Monetary.Value memory)
    {
        Monetary.Value memory supplyValue;
        Monetary.Value memory borrowValue;

        // Only adjust for liquidity if prompted AND if there is no override
        adjustForLiquidity = adjustForLiquidity && marginRatioOverride.value == 0;

        uint256 numMarkets = cache.getNumMarkets();
        for (uint256 i; i < numMarkets; ++i) {
            Types.Wei memory userWei = state.getWei(account, cache.getAtIndex(i).marketId, cache.getAtIndex(i).index);

            if (userWei.isZero()) {
                continue;
            }

            Decimal.D256 memory adjust = Decimal.one();
            if (adjustForLiquidity) {
                adjust = Decimal.onePlus(state.markets[cache.getAtIndex(i).marketId].marginPremium);
            }

            uint256 assetValue = userWei.value.mul(cache.getAtIndex(i).price.value);
            if (userWei.sign) {
                supplyValue.value = supplyValue.value.add(Decimal.div(assetValue, adjust));
            } else {
                borrowValue.value = borrowValue.value.add(Decimal.mul(assetValue, adjust));
            }
        }

        return (supplyValue, borrowValue);
    }

    function isCollateralized(
        Storage.State storage state,
        Account.Info memory account,
        Cache.MarketCache memory cache,
        bool requireMinBorrow
    )
        internal
        view
        returns (bool)
    {
        if (state.getAccountNumberOfMarketsWithDebt(account) == 0) {
            // The user does not have a balance with a borrow amount, so they must be collateralized
            return true;
        }

        // get account values (adjusted for liquidity, if there isn't a margin ratio override)
        (Decimal.D256 memory marginRatio,) = getAccountRiskOverride(state, account);
        (
            Monetary.Value memory supplyValue,
            Monetary.Value memory borrowValue
        ) = state.getAccountValues(
            account,
            cache,
            /* adjustForLiquidity = */ true,
            marginRatio
        );

        if (requireMinBorrow) {
            Require.that(
                borrowValue.value >= state.riskParams.minBorrowedValue.value,
                FILE,
                "Borrow value too low",
                account.owner,
                account.number
            );
        }

        if (marginRatio.value == 0) {
            marginRatio = state.riskParams.marginRatio;
        }

        uint256 requiredMargin = Decimal.mul(borrowValue.value, marginRatio);

        return supplyValue.value >= borrowValue.value.add(requiredMargin);
    }

    function isGlobalOperator(
        Storage.State storage state,
        address operator
    )
        internal
        view
        returns (bool)
    {
        return state.globalOperators[operator] == 1;
    }

    function isAutoTraderSpecial(
        Storage.State storage state,
        address autoTrader
    )
        internal
        view
        returns (bool)
    {
        return state.specialAutoTraders[autoTrader] == 1;
    }

    function isLocalOperator(
        Storage.State storage state,
        address owner,
        address operator
    )
        internal
        view
        returns (bool)
    {
        return state.operators[owner][operator] == 1;
    }

    function requireIsGlobalOperator(
        Storage.State storage state,
        address operator
    )
        internal
        view
    {
        bool isValidOperator = state.isGlobalOperator(operator);

        Require.that(
            isValidOperator,
            FILE,
            "Unpermissioned global operator",
            operator
        );
    }

    function requireIsOperator(
        Storage.State storage state,
        Account.Info memory account,
        address operator
    )
        internal
        view
    {
        bool isValidOperator =
            operator == account.owner
            || state.isGlobalOperator(operator)
            || state.isLocalOperator(account.owner, operator);

        Require.that(
            isValidOperator,
            FILE,
            "Unpermissioned operator",
            operator
        );
    }

    function getAccountRiskOverride(
        Storage.State storage state,
        Account.Info memory account
    )
        internal
        view
        returns (Decimal.D256 memory marginRatioOverride, Decimal.D256 memory liquidationSpreadOverride)
    {
        IAccountRiskOverrideSetter riskOverrideSetter = state.riskParams.accountRiskOverrideSetterMap[account.owner];
        if (address(riskOverrideSetter) != address(0)) {
            (marginRatioOverride, liquidationSpreadOverride) = riskOverrideSetter.getAccountRiskOverride(account);
            validateAccountRiskOverrideValues(state, marginRatioOverride, liquidationSpreadOverride);
            return (marginRatioOverride, liquidationSpreadOverride);
        }

        riskOverrideSetter = state.riskParams.defaultAccountRiskOverrideSetter;
        if (address(riskOverrideSetter) != address(0)) {
            (marginRatioOverride, liquidationSpreadOverride) = riskOverrideSetter.getAccountRiskOverride(account);
            validateAccountRiskOverrideValues(state, marginRatioOverride, liquidationSpreadOverride);
            return (marginRatioOverride, liquidationSpreadOverride);
        } else {
            marginRatioOverride = Decimal.zero();
            liquidationSpreadOverride = Decimal.zero();
            return (marginRatioOverride, liquidationSpreadOverride);
        }
    }

    /**
     * Determine and set an account's balance based on the intended balance change. Return the
     * equivalent amount in wei
     */
    function getNewParAndDeltaWei(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId,
        Interest.Index memory index,
        Types.AssetAmount memory amount
    )
        internal
        view
        returns (Types.Par memory, Types.Wei memory)
    {
        Types.Par memory oldPar = state.getPar(account, marketId);

        if (amount.value == 0 && amount.ref == Types.AssetReference.Delta) {
            return (oldPar, Types.zeroWei());
        }

        Types.Wei memory oldWei = Interest.parToWei(oldPar, index);
        Types.Par memory newPar;
        Types.Wei memory deltaWei;

        if (amount.denomination == Types.AssetDenomination.Wei) {
            deltaWei = Types.Wei({
                sign: amount.sign,
                value: amount.value
            });
            if (amount.ref == Types.AssetReference.Target) {
                deltaWei = deltaWei.sub(oldWei);
            }
            newPar = Interest.weiToPar(oldWei.add(deltaWei), index);
        } else { // AssetDenomination.Par
            newPar = Types.Par({
                sign: amount.sign,
                value: amount.value.to128()
            });
            if (amount.ref == Types.AssetReference.Delta) {
                newPar = oldPar.add(newPar);
            }
            deltaWei = Interest.parToWei(newPar, index).sub(oldWei);
        }

        return (newPar, deltaWei);
    }

    function getNewParAndDeltaWeiForLiquidation(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId,
        Interest.Index memory index,
        Types.AssetAmount memory amount
    )
        internal
        view
        returns (Types.Par memory, Types.Wei memory)
    {
        Types.Par memory oldPar = state.getPar(account, marketId);

        Require.that(
            !oldPar.isPositive(),
            FILE,
            "Owed balance cannot be positive",
            account.owner,
            account.number
        );

        (
            Types.Par memory newPar,
            Types.Wei memory deltaWei
        ) = state.getNewParAndDeltaWei(
            account,
            marketId,
            index,
            amount
        );

        // if attempting to over-repay the owed asset, bound it by the maximum
        if (newPar.isPositive()) {
            newPar = Types.zeroPar();
            deltaWei = state.getWei(account, marketId, index).negative();
        }

        Require.that(
            !deltaWei.isNegative() && oldPar.value >= newPar.value,
            FILE,
            "Owed balance cannot increase",
            account.owner,
            account.number
        );

        // if not paying back enough wei to repay any par, then bound wei to zero
        if (oldPar.equals(newPar)) {
            deltaWei = Types.zeroWei();
        }

        return (newPar, deltaWei);
    }

    function isVaporizable(
        Storage.State storage state,
        Account.Info memory account,
        Cache.MarketCache memory cache
    )
        internal
        view
        returns (bool)
    {
        bool hasNegative = false;
        uint256 numMarkets = cache.getNumMarkets();
        for (uint256 i; i < numMarkets; ++i) {
            Types.Par memory par = state.getPar(account, cache.getAtIndex(i).marketId);
            if (par.isZero()) {
                continue;
            } else if (par.sign) {
                return false;
            } else {
                hasNegative = true;
            }
        }
        return hasNegative;
    }

    function validateAccountRiskOverrideValues(
        Storage.State storage state,
        Decimal.D256 memory marginRatioOverride,
        Decimal.D256 memory liquidationSpreadOverride
    ) internal view {
        Require.that(
            marginRatioOverride.value <= state.riskLimits.marginRatioMax,
            FILE,
            "Ratio too high"
        );
        Require.that(
            liquidationSpreadOverride.value <= state.riskLimits.liquidationSpreadMax,
            FILE,
            "Spread too high"
        );

        if (marginRatioOverride.value != 0 && liquidationSpreadOverride.value != 0) {
            Require.that(
                liquidationSpreadOverride.value < marginRatioOverride.value,
                FILE,
                "Spread cannot be >= ratio"
            );
        } else {
            Require.that(
                liquidationSpreadOverride.value == 0 && marginRatioOverride.value == 0,
                FILE,
                "Spread and ratio must both be 0"
            );
        }
    }

    // =============== Setter Functions ===============

    function updateIndex(
        Storage.State storage state,
        uint256 marketId
    )
        internal
        returns (Interest.Index memory)
    {
        Interest.Index memory index = state.getIndex(marketId);
        if (index.lastUpdate == Time.currentTime()) {
            return index;
        }
        return state.markets[marketId].index = state.fetchNewIndex(marketId, index);
    }

    function setStatus(
        Storage.State storage state,
        Account.Info memory account,
        Account.Status status
    )
        internal
    {
        state.accounts[account.owner][account.number].status = status;
    }

    function setPar(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId,
        Types.Par memory newPar
    )
        internal
    {
        Types.Par memory oldPar = state.getPar(account, marketId);

        if (Types.equals(oldPar, newPar)) {
            // GUARD statement
            return;
        }

        // updateTotalPar
        Types.TotalPar memory totalPar = state.getTotalPar(marketId);

        // roll-back oldPar
        if (oldPar.sign) {
            totalPar.supply = uint256(totalPar.supply).sub(oldPar.value).to128();
        } else {
            totalPar.borrow = uint256(totalPar.borrow).sub(oldPar.value).to128();
        }

        // roll-forward newPar
        if (newPar.sign) {
            totalPar.supply = uint256(totalPar.supply).add(newPar.value).to128();
        } else {
            totalPar.borrow = uint256(totalPar.borrow).add(newPar.value).to128();
        }

        if (oldPar.isLessThanZero() && newPar.isGreaterThanOrEqualToZero()) {
            // user went from borrowing to repaying or positive
            state.accounts[account.owner][account.number].numberOfMarketsWithDebt -= 1;
        } else if (oldPar.isGreaterThanOrEqualToZero() && newPar.isLessThanZero()) {
            // user went from zero or positive to borrowing
            state.accounts[account.owner][account.number].numberOfMarketsWithDebt += 1;
        }

        if (newPar.isZero() && (!oldPar.isZero())) {
            // User went from a non-zero balance to zero. Remove the market from the set.
            state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.remove(marketId);
        } else if ((!newPar.isZero()) && oldPar.isZero()) {
            // User went from zero to non-zero. Add the market to the set.
            state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.add(marketId);
        }

        state.markets[marketId].totalPar = totalPar;
        state.accounts[account.owner][account.number].balances[marketId] = newPar;
    }

    /**
     * Determine and set an account's balance based on a change in wei
     */
    function setParFromDeltaWei(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId,
        Interest.Index memory index,
        Types.Wei memory deltaWei
    )
        internal
    {
        if (deltaWei.isZero()) {
            return;
        }
        Types.Wei memory oldWei = state.getWei(account, marketId, index);
        Types.Wei memory newWei = oldWei.add(deltaWei);
        Types.Par memory newPar = Interest.weiToPar(newWei, index);
        state.setPar(
            account,
            marketId,
            newPar
        );
    }

    /**
     * Initializes the cache using the set bits
     */
    function initializeCache(
        Storage.State storage state,
        Cache.MarketCache memory cache,
        bool fetchFreshIndex
    ) internal view {
        cache.markets = new Cache.MarketInfo[](cache.marketsLength);

        // Really neat byproduct of iterating through a bitmap using the least significant bit, where each set flag
        // represents the marketId, --> the initialized `cache.markets` array is sorted in O(n)!
        // Meaning, this function call is O(n) where `n` is the number of markets in the cache
        uint256 marketBitmapsLength = cache.marketBitmaps.length;
        for (uint256 i; i < marketBitmapsLength; ++i) {
            uint256 bitmap = cache.marketBitmaps[i];
            while (bitmap != 0) {
                uint256 nextSetBit = Bits.getLeastSignificantBit(bitmap);
                uint256 marketId = Bits.getMarketIdFromBit(i, nextSetBit);
                address token = state.getToken(marketId);
                Types.TotalPar memory totalPar = state.getTotalPar(marketId);
                Interest.Index memory index = state.getIndex(marketId);
                cache.markets[cache.counter++] = Cache.MarketInfo({
                    marketId: marketId,
                    token: token,
                    isClosing: state.markets[marketId].isClosing,
                    borrowPar: totalPar.borrow,
                    supplyPar: totalPar.supply,
                    index: fetchFreshIndex ? state.fetchNewIndex(marketId, index) : index,
                    price: state.fetchPrice(marketId, token)
                });

                // unset the set bit
                bitmap = Bits.unsetBit(bitmap, nextSetBit);
            }
            if (cache.counter == cache.marketsLength) {
                break;
            }
        }

        assert(cache.marketsLength == cache.counter);
    }
}

File 12 of 50 : SafeExternalCallback.sol
/*

    Copyright 2022 Dolomite.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";

import { IExternalCallback } from "../interfaces/IExternalCallback.sol";
import { Account } from "../lib/Account.sol";
import { ExcessivelySafeCall } from "../lib/ExcessivelySafeCall.sol";
import { DolomiteMarginMath } from "../lib/DolomiteMarginMath.sol";
import { Types } from "../lib/Types.sol";


library SafeExternalCallback {
    using Address for address;
    using ExcessivelySafeCall for address;

    // ============ Events ============

    event LogExternalCallbackSuccess(address indexed primaryAccountOwner, uint primaryAccountNumber);

    event LogExternalCallbackFailure(address indexed primaryAccountOwner, uint primaryAccountNumber, string reason);

    // ============ Functions ============

    function callInternalBalanceChangeIfNecessary(
        Account.Info memory _primaryAccount,
        Account.Info memory _secondaryAccount,
        uint256 _primaryMarket,
        Types.Wei memory _primaryDeltaWei,
        uint256 _secondaryMarket,
        Types.Wei memory _secondaryDeltaWei,
        uint256 _gasLimit
    ) internal {
        if (_primaryAccount.owner.isContract()) {
            uint16 maxCopyBytes = 256;
            (bool isCallSuccessful, bytes memory result) = _primaryAccount.owner.excessivelySafeCall(
                /* _gas = */ Math.min(gasleft(), _gasLimit), // send, at most, `_gasLimit` to the callback
                maxCopyBytes, // receive at-most this many bytes worth of return data
                abi.encodeWithSelector(
                    IExternalCallback(_primaryAccount.owner).onInternalBalanceChange.selector,
                    _primaryAccount.number,
                    _secondaryAccount,
                    _primaryMarket,
                    _primaryDeltaWei,
                    _secondaryMarket,
                    _secondaryDeltaWei
                )
            );

            if (isCallSuccessful) {
                emit LogExternalCallbackSuccess(_primaryAccount.owner, _primaryAccount.number);
            } else {
                // For reversions:
                // - the first 4 bytes is the method ID
                // - the next 32 bytes is the offset (hardcoded 0x20)
                // - the next 32 bytes is the length of the string
                // Here is an example result. The first 68 bytes (136 hexadecimal characters) are the templated
                // 08c379a0                                                         // erroring method ID
                // 0000000000000000000000000000000000000000000000000000000000000020 // offset to where string is
                // 0000000000000000000000000000000000000000000000000000000000000001 // string length
                // 2100000000000000000000000000000000000000000000000000000000000000 // string itself - not templated
                if (result.length < 68) {
                    result = bytes("");
                } else {
                    // parse the result bytes error message into a human-readable string
                    uint length;
                    // solium-disable-next-line security/no-inline-assembly
                    assembly {
                        result := add(result, 0x04)
                        length := mload(add(result, 0x40))
                        if gt(length, sub(maxCopyBytes, 0x44)) {
                            // if the length from `result` is longer than the max length, subtract the 68 bytes
                            // from the maxCopyBytes
                            mstore(add(result, 0x40), sub(maxCopyBytes, 0x44))
                        }
                    }
                    result = bytes(abi.decode(result, (string)));
                }
                emit LogExternalCallbackFailure(
                    _primaryAccount.owner,
                    _primaryAccount.number,
                    string(result)
                );
            }
        }
    }

}

File 13 of 50 : Require.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;


/**
 * @title Require
 * @author dYdX
 *
 * Stringifies parameters to pretty-print revert messages. Costs more gas than regular require()
 */
library Require {

    // ============ Constants ============

    uint256 constant ASCII_ZERO = 48; // '0'
    uint256 constant ASCII_RELATIVE_ZERO = 87; // 'a' - 10
    uint256 constant ASCII_LOWER_EX = 120; // 'x'
    bytes2 constant COLON = 0x3a20; // ': '
    bytes2 constant COMMA = 0x2c20; // ', '
    bytes2 constant LPAREN = 0x203c; // ' <'
    byte constant RPAREN = 0x3e; // '>'
    uint256 constant FOUR_BIT_MASK = 0xf;

    // ============ Library Functions ============

    function that(
        bool must,
        bytes32 file,
        bytes32 reason
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason)
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        uint256 payloadA
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        uint256 payloadA,
        uint256 payloadB
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        COMMA,
                        stringify(payloadB),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA,
        uint256 payloadB
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        COMMA,
                        stringify(payloadB),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA,
        uint256 payloadB,
        uint256 payloadC
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        COMMA,
                        stringify(payloadB),
                        COMMA,
                        stringify(payloadC),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        bytes32 payloadA
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        RPAREN
                    )
                )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        bytes32 payloadA,
        uint256 payloadB,
        uint256 payloadC
    )
        internal
        pure
    {
        if (!must) {
            revert(
                string(
                    abi.encodePacked(
                        stringifyTruncated(file),
                        COLON,
                        stringifyTruncated(reason),
                        LPAREN,
                        stringify(payloadA),
                        COMMA,
                        stringify(payloadB),
                        COMMA,
                        stringify(payloadC),
                        RPAREN
                    )
                )
            );
        }
    }

    // ============ Private Functions ============

    function stringifyTruncated(
        bytes32 input
    )
        internal
        pure
        returns (bytes memory)
    {
        // put the input bytes into the result
        bytes memory result = abi.encodePacked(input);

        // determine the length of the input by finding the location of the last non-zero byte
        for (uint256 i = 32; i != 0; ) {
            // reverse-for-loops with unsigned integer
            /* solium-disable-next-line security/no-modify-for-iter-var */
            i--;

            // find the last non-zero byte in order to determine the length
            if (result[i] != 0) {
                uint256 length = i + 1;

                /* solium-disable-next-line security/no-inline-assembly */
                assembly {
                    mstore(result, length) // r.length = length;
                }

                return result;
            }
        }

        // all bytes are zero
        return new bytes(0);
    }

    function stringify(
        uint256 input
    )
        private
        pure
        returns (bytes memory)
    {
        if (input == 0) {
            return "0";
        }

        // get the final string length
        uint256 j = input;
        uint256 length;
        while (j != 0) {
            ++length;
            j /= 10;
        }

        // allocate the string
        bytes memory bstr = new bytes(length);

        // populate the string starting with the least-significant character
        j = input;
        for (uint256 i = length; i != 0; ) {
            // reverse-for-loops with unsigned integer
            /* solium-disable-next-line security/no-modify-for-iter-var */
            i--;

            // take last decimal digit
            bstr[i] = byte(uint8(ASCII_ZERO + (j % 10)));

            // remove the last decimal digit
            j /= 10;
        }

        return bstr;
    }

    function stringify(
        address input
    )
        private
        pure
        returns (bytes memory)
    {
        uint256 z = uint256(input);

        // addresses are "0x" followed by 20 bytes of data which take up 2 characters each
        bytes memory result = new bytes(42);

        // populate the result with "0x"
        result[0] = byte(uint8(ASCII_ZERO));
        result[1] = byte(uint8(ASCII_LOWER_EX));

        // for each byte (starting from the lowest byte), populate the result with two characters
        for (uint256 i; i < 20; ++i) {
            // each byte takes two characters
            uint256 shift = i * 2;

            // populate the least-significant character
            result[41 - shift] = char(z & FOUR_BIT_MASK);
            z = z >> 4;

            // populate the most-significant character
            result[40 - shift] = char(z & FOUR_BIT_MASK);
            z = z >> 4;
        }

        return result;
    }

    function stringify(
        bytes32 input
    )
        private
        pure
        returns (bytes memory)
    {
        uint256 z = uint256(input);

        // bytes32 are "0x" followed by 32 bytes of data which take up 2 characters each
        bytes memory result = new bytes(66);

        // populate the result with "0x"
        result[0] = byte(uint8(ASCII_ZERO));
        result[1] = byte(uint8(ASCII_LOWER_EX));

        // for each byte (starting from the lowest byte), populate the result with two characters
        for (uint256 i; i < 32; ++i) {
            // each byte takes two characters
            uint256 shift = i * 2;

            // populate the least-significant character
            result[65 - shift] = char(z & FOUR_BIT_MASK);
            z = z >> 4;

            // populate the most-significant character
            result[64 - shift] = char(z & FOUR_BIT_MASK);
            z = z >> 4;
        }

        return result;
    }

    function char(
        uint256 input
    )
        private
        pure
        returns (byte)
    {
        // return ASCII digit (0-9)
        if (input < 10) {
            return byte(uint8(input + ASCII_ZERO));
        }

        // return ASCII letter (a-f)
        return byte(uint8(input + ASCII_RELATIVE_ZERO));
    }
}

File 14 of 50 : Monetary.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;


/**
 * @title Monetary
 * @author dYdX
 *
 * Library for types involving money
 */
library Monetary {

    /*
     * The price of a base-unit of an asset. Has `36 - token.decimals` decimals
     */
    struct Price {
        uint256 value;
    }

    /*
     * Total value of an some amount of an asset. Equal to (price * amount). Has 36 decimals.
     */
    struct Value {
        uint256 value;
    }
}

File 15 of 50 : Interest.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Decimal } from "./Decimal.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
import { Time } from "./Time.sol";
import { Types } from "./Types.sol";


/**
 * @title Interest
 * @author dYdX
 *
 * Library for managing the interest rate and interest indexes of DolomiteMargin
 */
library Interest {
    using DolomiteMarginMath for uint256;
    using SafeMath for uint256;

    // ============ Constants ============

    bytes32 private constant FILE = "Interest";
    uint64 constant BASE = 10**18;

    // ============ Structs ============

    struct Rate {
        uint256 value;
    }

    struct Index {
        uint112 borrow;
        uint112 supply;
        uint32 lastUpdate;
    }

    // ============ Library Functions ============

    /**
     * Get a new market Index based on the old index and market interest rate.
     * Calculate interest for borrowers by using the formula rate * time. Approximates
     * continuously-compounded interest when called frequently, but is much more
     * gas-efficient to calculate. For suppliers, the interest rate is adjusted by the earningsRate,
     * then prorated across all suppliers.
     *
     * @param  index         The old index for a market
     * @param  rate          The current interest rate of the market
     * @param  totalPar      The total supply and borrow par values of the market
     * @param  earningsRate  The portion of the interest that is forwarded to the suppliers
     * @return               The updated index for a market
     */
    function calculateNewIndex(
        Index memory index,
        Rate memory rate,
        Types.TotalPar memory totalPar,
        Decimal.D256 memory earningsRate
    )
        internal
        view
        returns (Index memory)
    {
        (
            Types.Wei memory supplyWei,
            Types.Wei memory borrowWei
        ) = totalParToWei(totalPar, index);

        // get interest increase for borrowers
        uint32 currentTime = Time.currentTime();
        uint256 borrowInterest = rate.value.mul(uint256(currentTime).sub(index.lastUpdate));

        // get interest increase for suppliers
        uint256 supplyInterest;
        if (Types.isZero(supplyWei)) {
            supplyInterest = 0;
        } else {
            supplyInterest = Decimal.mul(borrowInterest, earningsRate);
            if (borrowWei.value < supplyWei.value) {
                // scale down the interest by the amount being supplied. Why? Because interest is only being paid on
                // the borrowWei, which means it's split amongst all of the supplyWei. Scaling it down normalizes it
                // for the suppliers to share what's being paid by borrowers
                supplyInterest = DolomiteMarginMath.getPartial(supplyInterest, borrowWei.value, supplyWei.value);
            }
        }
        assert(supplyInterest <= borrowInterest);

        return Index({
            borrow: DolomiteMarginMath.getPartial(index.borrow, borrowInterest, BASE).add(index.borrow).to112(),
            supply: DolomiteMarginMath.getPartial(index.supply, supplyInterest, BASE).add(index.supply).to112(),
            lastUpdate: currentTime
        });
    }

    function newIndex()
        internal
        view
        returns (Index memory)
    {
        return Index({
            borrow: BASE,
            supply: BASE,
            lastUpdate: Time.currentTime()
        });
    }

    /*
     * Convert a principal amount to a token amount given an index.
     */
    function parToWei(
        Types.Par memory input,
        Index memory index
    )
        internal
        pure
        returns (Types.Wei memory)
    {
        uint256 inputValue = uint256(input.value);
        if (input.sign) {
            return Types.Wei({
                sign: true,
                value: inputValue.getPartialRoundHalfUp(index.supply, BASE)
            });
        } else {
            return Types.Wei({
                sign: false,
                value: inputValue.getPartialRoundHalfUp(index.borrow, BASE)
            });
        }
    }

    /*
     * Convert a token amount to a principal amount given an index.
     */
    function weiToPar(
        Types.Wei memory input,
        Index memory index
    )
        internal
        pure
        returns (Types.Par memory)
    {
        if (input.sign) {
            return Types.Par({
                sign: true,
                value: input.value.getPartialRoundHalfUp(BASE, index.supply).to128()
            });
        } else {
            return Types.Par({
                sign: false,
                value: input.value.getPartialRoundHalfUp(BASE, index.borrow).to128()
            });
        }
    }

    /*
     * Convert the total supply and borrow principal amounts of a market to total supply and borrow
     * token amounts.
     */
    function totalParToWei(
        Types.TotalPar memory totalPar,
        Index memory index
    )
        internal
        pure
        returns (Types.Wei memory, Types.Wei memory)
    {
        Types.Par memory supplyPar = Types.Par({
            sign: true,
            value: totalPar.supply
        });
        Types.Par memory borrowPar = Types.Par({
            sign: false,
            value: totalPar.borrow
        });
        Types.Wei memory supplyWei = parToWei(supplyPar, index);
        Types.Wei memory borrowWei = parToWei(borrowPar, index);
        return (supplyWei, borrowWei);
    }
}

File 16 of 50 : Exchange.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Require } from "./Require.sol";
import { Token } from "./Token.sol";
import { Types } from "./Types.sol";
import { IExchangeWrapper } from "../interfaces/IExchangeWrapper.sol";


/**
 * @title Exchange
 * @author dYdX
 *
 * Library for transferring tokens and interacting with ExchangeWrappers by using the Wei struct
 */
library Exchange {
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "Exchange";

    // ============ Library Functions ============

    function transferOut(
        address token,
        address to,
        Types.Wei memory deltaWei
    )
        internal
    {
        Require.that(
            !deltaWei.isPositive(),
            FILE,
            "Cannot transferOut positive",
            deltaWei.value
        );

        Token.transfer(
            token,
            to,
            deltaWei.value
        );
    }

    function transferIn(
        address token,
        address from,
        Types.Wei memory deltaWei
    )
        internal
    {
        Require.that(
            !deltaWei.isNegative(),
            FILE,
            "Cannot transferIn negative",
            deltaWei.value
        );

        Token.transferFrom(
            token,
            from,
            address(this),
            deltaWei.value
        );
    }

    function getCost(
        address exchangeWrapper,
        address supplyToken,
        address borrowToken,
        Types.Wei memory desiredAmount,
        bytes memory orderData
    )
        internal
        view
        returns (Types.Wei memory)
    {
        Require.that(
            !desiredAmount.isNegative(),
            FILE,
            "Cannot getCost negative",
            desiredAmount.value
        );

        Types.Wei memory result;
        result.sign = false;
        result.value = IExchangeWrapper(exchangeWrapper).getExchangeCost(
            supplyToken,
            borrowToken,
            desiredAmount.value,
            orderData
        );

        return result;
    }

    function exchange(
        address exchangeWrapper,
        address accountOwner,
        address supplyToken,
        address borrowToken,
        Types.Wei memory requestedFillAmount,
        bytes memory orderData
    )
        internal
        returns (Types.Wei memory)
    {
        Require.that(
            !requestedFillAmount.isPositive(),
            FILE,
            "Cannot exchange positive",
            requestedFillAmount.value
        );

        transferOut(borrowToken, exchangeWrapper, requestedFillAmount);

        Types.Wei memory result;
        result.sign = true;
        result.value = IExchangeWrapper(exchangeWrapper).exchange(
            accountOwner,
            address(this),
            supplyToken,
            borrowToken,
            requestedFillAmount.value,
            orderData
        );

        transferIn(supplyToken, exchangeWrapper, result);

        return result;
    }
}

File 17 of 50 : ExcessivelySafeCall.sol
/*

    Copyright 2022 Dolomite.

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

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

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

*/

pragma solidity ^0.5.7;


/**
 * @author https://github.com/nomad-xyz
 */
library ExcessivelySafeCall {

    function safeStaticCall(
        address _target,
        bytes4 _selector,
        bytes memory _calldata
    ) internal view returns (bool isSuccess, bytes memory returnData) {
        (isSuccess, returnData) = _target.staticcall(abi.encodeWithSelector(_selector, _calldata));
    }

    /// @notice Use when you _really_ really _really_ don't trust the called
    /// contract. This prevents the called contract from causing reversion of
    /// the caller in as many ways as we can.
    /// @dev The main difference between this and a solidity low-level call is
    /// that we limit the number of bytes that the callee can cause to be
    /// copied to caller memory. This prevents stupid things like malicious
    /// contracts returning 10,000,000 bytes causing a local OOG when copying
    /// to memory.
    /// @param _target The address to call
    /// @param _gas The amount of gas to forward to the remote contract
    /// @param _maxCopy The maximum number of bytes of returndata to copy
    /// to memory.
    /// @param _calldata The data to send to the remote contract
    /// @return success and returndata, as `.call()`. Returndata is capped to
    /// `_maxCopy` bytes.
    function excessivelySafeCall(
        address _target,
        uint256 _gas,
        uint16 _maxCopy,
        bytes memory _calldata
    ) internal returns (bool, bytes memory) {
        // set up for assembly call
        uint256 _toCopy;
        bool _success;
        bytes memory _returndata = new bytes(_maxCopy);
        // dispatch message to recipient
        // by assembly calling "handle" function
        // we call via assembly to avoid mem-copying a very large returnData
        // returned by a malicious contract
        /* solium-disable security/no-inline-assembly */
        assembly {
            _success := call(
                _gas, // gas
                _target, // recipient
                0, // ether value
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
            )
            // limit our copy to 256 bytes
            _toCopy := returndatasize()
            if gt(_toCopy, _maxCopy) {
                _toCopy := _maxCopy
            }
            // Store the length of the copied bytes
            mstore(_returndata, _toCopy)
            // copy the bytes from returndata[0:_toCopy]
            returndatacopy(add(_returndata, 0x20), 0, _toCopy)
        }
        /* solium-enable security/no-inline-assembly */
        return (_success, _returndata);
    }
}

File 18 of 50 : Events.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Account } from "./Account.sol";
import { Actions } from "./Actions.sol";
import { Cache } from "./Cache.sol";
import { Interest } from "./Interest.sol";
import { Monetary } from "./Monetary.sol";
import { Storage } from "./Storage.sol";
import { Types } from "./Types.sol";


/**
 * @title Events
 * @author dYdX
 *
 * Library to parse and emit logs from which the state of all accounts and indexes can be followed
 */
library Events {
    using Types for Types.Wei;
    using Storage for Storage.State;

    // ============ Events ============

    event LogIndexUpdate(
        uint256 indexed market,
        Interest.Index index
    );

    event LogOraclePrice(
        uint256 indexed market,
        Monetary.Price price
    );

    event LogInterestRate(
        uint256 indexed market,
        Interest.Rate rate
    );

    event LogOperation(
        address sender
    );

    event LogDeposit(
        address indexed accountOwner,
        uint256 accountNumber,
        uint256 market,
        BalanceUpdate update,
        address from
    );

    event LogWithdraw(
        address indexed accountOwner,
        uint256 accountNumber,
        uint256 market,
        BalanceUpdate update,
        address to
    );

    event LogTransfer(
        address indexed accountOneOwner,
        uint256 accountOneNumber,
        address indexed accountTwoOwner,
        uint256 accountTwoNumber,
        uint256 market,
        BalanceUpdate updateOne,
        BalanceUpdate updateTwo
    );

    event LogBuy(
        address indexed accountOwner,
        uint256 accountNumber,
        uint256 takerMarket,
        uint256 makerMarket,
        BalanceUpdate takerUpdate,
        BalanceUpdate makerUpdate,
        address exchangeWrapper
    );

    event LogSell(
        address indexed accountOwner,
        uint256 accountNumber,
        uint256 takerMarket,
        uint256 makerMarket,
        BalanceUpdate takerUpdate,
        BalanceUpdate makerUpdate,
        address exchangeWrapper
    );

    event LogTrade(
        address indexed takerAccountOwner,
        uint256 takerAccountNumber,
        address indexed makerAccountOwner,
        uint256 makerAccountNumber,
        uint256 inputMarket,
        uint256 outputMarket,
        BalanceUpdate takerInputUpdate,
        BalanceUpdate takerOutputUpdate,
        BalanceUpdate makerInputUpdate,
        BalanceUpdate makerOutputUpdate,
        address autoTrader
    );

    event LogCall(
        address indexed accountOwner,
        uint256 accountNumber,
        address callee
    );

    event LogLiquidate(
        address indexed solidAccountOwner,
        uint256 solidAccountNumber,
        address indexed liquidAccountOwner,
        uint256 liquidAccountNumber,
        uint256 heldMarket,
        uint256 owedMarket,
        BalanceUpdate solidHeldUpdate,
        BalanceUpdate solidOwedUpdate,
        BalanceUpdate liquidHeldUpdate,
        BalanceUpdate liquidOwedUpdate
    );

    event LogVaporize(
        address indexed solidAccountOwner,
        uint256 solidAccountNumber,
        address indexed vaporAccountOwner,
        uint256 vaporAccountNumber,
        uint256 heldMarket,
        uint256 owedMarket,
        BalanceUpdate solidHeldUpdate,
        BalanceUpdate solidOwedUpdate,
        BalanceUpdate vaporOwedUpdate
    );

    // ============ Structs ============

    struct BalanceUpdate {
        Types.Wei deltaWei;
        Types.Par newPar;
    }

    // ============ Internal Functions ============

    function logIndexUpdate(
        uint256 marketId,
        Interest.Index memory index
    )
        internal
    {
        emit LogIndexUpdate(
            marketId,
            index
        );
    }

    function logOraclePrice(
        Cache.MarketInfo memory marketInfo
    )
        internal
    {
        emit LogOraclePrice(
            marketInfo.marketId,
            marketInfo.price
        );
    }

    function logInterestRate(
        uint256 marketId,
        Interest.Rate memory rate
    )
        internal
    {
        emit LogInterestRate(
            marketId,
            rate
        );
    }

    function logOperation()
        internal
    {
        emit LogOperation(msg.sender);
    }

    function logDeposit(
        Storage.State storage state,
        Actions.DepositArgs memory args,
        Types.Wei memory deltaWei
    )
        internal
    {
        emit LogDeposit(
            args.account.owner,
            args.account.number,
            args.market,
            getBalanceUpdate(
                state,
                args.account,
                args.market,
                deltaWei
            ),
            args.from
        );
    }

    function logWithdraw(
        Storage.State storage state,
        Actions.WithdrawArgs memory args,
        Types.Wei memory deltaWei
    )
        internal
    {
        emit LogWithdraw(
            args.account.owner,
            args.account.number,
            args.market,
            getBalanceUpdate(
                state,
                args.account,
                args.market,
                deltaWei
            ),
            args.to
        );
    }

    function logTransfer(
        Storage.State storage state,
        Actions.TransferArgs memory args,
        Types.Wei memory deltaWei
    )
        internal
    {
        emit LogTransfer(
            args.accountOne.owner,
            args.accountOne.number,
            args.accountTwo.owner,
            args.accountTwo.number,
            args.market,
            getBalanceUpdate(
                state,
                args.accountOne,
                args.market,
                deltaWei
            ),
            getBalanceUpdate(
                state,
                args.accountTwo,
                args.market,
                deltaWei.negative()
            )
        );
    }

    function logBuy(
        Storage.State storage state,
        Actions.BuyArgs memory args,
        Types.Wei memory takerWei,
        Types.Wei memory makerWei
    )
        internal
    {
        emit LogBuy(
            args.account.owner,
            args.account.number,
            args.takerMarket,
            args.makerMarket,
            getBalanceUpdate(
                state,
                args.account,
                args.takerMarket,
                takerWei
            ),
            getBalanceUpdate(
                state,
                args.account,
                args.makerMarket,
                makerWei
            ),
            args.exchangeWrapper
        );
    }

    function logSell(
        Storage.State storage state,
        Actions.SellArgs memory args,
        Types.Wei memory takerWei,
        Types.Wei memory makerWei
    )
        internal
    {
        emit LogSell(
            args.account.owner,
            args.account.number,
            args.takerMarket,
            args.makerMarket,
            getBalanceUpdate(
                state,
                args.account,
                args.takerMarket,
                takerWei
            ),
            getBalanceUpdate(
                state,
                args.account,
                args.makerMarket,
                makerWei
            ),
            args.exchangeWrapper
        );
    }

    function logTrade(
        Storage.State storage state,
        Actions.TradeArgs memory args,
        Types.Wei memory inputWei,
        Types.Wei memory outputWei
    )
        internal
    {
        BalanceUpdate[4] memory updates = [
            getBalanceUpdate(
                state,
                args.takerAccount,
                args.inputMarket,
                inputWei.negative()
            ),
            getBalanceUpdate(
                state,
                args.takerAccount,
                args.outputMarket,
                outputWei.negative()
            ),
            getBalanceUpdate(
                state,
                args.makerAccount,
                args.inputMarket,
                inputWei
            ),
            getBalanceUpdate(
                state,
                args.makerAccount,
                args.outputMarket,
                outputWei
            )
        ];

        emit LogTrade(
            args.takerAccount.owner,
            args.takerAccount.number,
            args.makerAccount.owner,
            args.makerAccount.number,
            args.inputMarket,
            args.outputMarket,
            updates[0],
            updates[1],
            updates[2],
            updates[3],
            args.autoTrader
        );
    }

    function logCall(
        Actions.CallArgs memory args
    )
        internal
    {
        emit LogCall(
            args.account.owner,
            args.account.number,
            args.callee
        );
    }

    function logLiquidate(
        Storage.State storage state,
        Actions.LiquidateArgs memory args,
        Types.Wei memory heldWei,
        Types.Wei memory owedWei
    )
        internal
    {
        BalanceUpdate memory solidHeldUpdate = getBalanceUpdate(
            state,
            args.solidAccount,
            args.heldMarket,
            heldWei.negative()
        );
        BalanceUpdate memory solidOwedUpdate = getBalanceUpdate(
            state,
            args.solidAccount,
            args.owedMarket,
            owedWei.negative()
        );
        BalanceUpdate memory liquidHeldUpdate = getBalanceUpdate(
            state,
            args.liquidAccount,
            args.heldMarket,
            heldWei
        );
        BalanceUpdate memory liquidOwedUpdate = getBalanceUpdate(
            state,
            args.liquidAccount,
            args.owedMarket,
            owedWei
        );

        emit LogLiquidate(
            args.solidAccount.owner,
            args.solidAccount.number,
            args.liquidAccount.owner,
            args.liquidAccount.number,
            args.heldMarket,
            args.owedMarket,
            solidHeldUpdate,
            solidOwedUpdate,
            liquidHeldUpdate,
            liquidOwedUpdate
        );
    }

    function logVaporize(
        Storage.State storage state,
        Actions.VaporizeArgs memory args,
        Types.Wei memory heldWei,
        Types.Wei memory owedWei,
        Types.Wei memory excessWei
    )
        internal
    {
        BalanceUpdate memory solidHeldUpdate = getBalanceUpdate(
            state,
            args.solidAccount,
            args.heldMarket,
            heldWei.negative()
        );
        BalanceUpdate memory solidOwedUpdate = getBalanceUpdate(
            state,
            args.solidAccount,
            args.owedMarket,
            owedWei.negative()
        );
        BalanceUpdate memory vaporOwedUpdate = getBalanceUpdate(
            state,
            args.vaporAccount,
            args.owedMarket,
            owedWei.add(excessWei)
        );

        emit LogVaporize(
            args.solidAccount.owner,
            args.solidAccount.number,
            args.vaporAccount.owner,
            args.vaporAccount.number,
            args.heldMarket,
            args.owedMarket,
            solidHeldUpdate,
            solidOwedUpdate,
            vaporOwedUpdate
        );
    }

    // ============ Private Functions ============

    function getBalanceUpdate(
        Storage.State storage state,
        Account.Info memory account,
        uint256 market,
        Types.Wei memory deltaWei
    )
        private
        view
        returns (BalanceUpdate memory)
    {
        return BalanceUpdate({
            deltaWei: deltaWei,
            newPar: state.getPar(account, market)
        });
    }
}

File 19 of 50 : EnumerableSet.sol
/*

    Copyright 2021 Dolomite.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;


library EnumerableSet {

    struct Set {
        // Storage of set values
        uint256[] _values;
        // Value to the index in `_values` array, plus 1 because index 0 means a value is not in the set.
        mapping(uint256 => uint256) _valueToIndexMap;
    }

    /**
    * @dev Add a value to a set. O(1).
     *
     * @return true if the value was added to the set, that is if it was not already present.
     */
    function add(Set storage set, uint256 value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._valueToIndexMap[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * @return true if the value was removed from the set, that is if it was present.
     */
    function remove(Set storage set, uint256 value) internal returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._valueToIndexMap[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                uint256 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._valueToIndexMap[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored, which is the last index
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._valueToIndexMap[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Set storage set, uint256 value) internal view returns (bool) {
        return set._valueToIndexMap[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(Set storage set) internal view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value at the corresponding index. O(1).
     */
    function getAtIndex(Set storage set, uint256 index) internal view returns (uint256) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Set storage set) internal view returns (uint256[] memory) {
        return set._values;
    }

}

File 20 of 50 : DolomiteMarginMath.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Require } from "./Require.sol";


/**
 * @title Math
 * @author dYdX
 *
 * Library for non-standard Math functions
 */
library DolomiteMarginMath {
    using SafeMath for uint256;

    // ============ Constants ============

    bytes32 private constant FILE = "Math";

    // ============ Library Functions ============

    /*
     * Return target * (numerator / denominator).
     */
    function getPartial(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
        internal
        pure
        returns (uint256)
    {
        return target.mul(numerator).div(denominator);
    }

    /*
     * Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.1 rounds to 102
     * instead of 101.
     */
    function getPartialRoundUp(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
        internal
        pure
        returns (uint256)
    {
        if (target == 0 || numerator == 0) {
            // SafeMath will check for zero denominator
            return SafeMath.div(0, denominator);
        }
        return target.mul(numerator).sub(1).div(denominator).add(1);
    }

    /*
     * Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.5 rounds to 102
     * instead of 101.
     */
    function getPartialRoundHalfUp(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
        internal
        pure
        returns (uint256)
    {
        if (target == 0 || numerator == 0) {
            // SafeMath will check for zero denominator
            return SafeMath.div(0, denominator);
        }
        uint result = target.mul(numerator);
        // round the denominator comparator up to ensure a fair comparison is done on the `result`'s modulo.
        // For example, 51 / 103 == 0; 51 % 103 == 51; ((103 - 1) / 2) + 1 == 52; 51 < 52, therefore no round up
        return result.div(denominator).add(result.mod(denominator) >= denominator.sub(1).div(2).add(1) ? 1 : 0);
    }

    function to128(
        uint256 number
    )
        internal
        pure
        returns (uint128)
    {
        uint128 result = uint128(number);
        Require.that(
            result == number,
            FILE,
            "Unsafe cast to uint128",
            number
        );
        return result;
    }

    function to112(
        uint256 number
    )
        internal
        pure
        returns (uint112)
    {
        uint112 result = uint112(number);
        Require.that(
            result == number,
            FILE,
            "Unsafe cast to uint112",
            number
        );
        return result;
    }

    function to32(
        uint256 number
    )
        internal
        pure
        returns (uint32)
    {
        uint32 result = uint32(number);
        Require.that(
            result == number,
            FILE,
            "Unsafe cast to uint32",
            number
        );
        return result;
    }
}

File 21 of 50 : Decimal.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";


/**
 * @title Decimal
 * @author dYdX
 *
 * Library that defines a fixed-point number with 18 decimal places.
 */
library Decimal {
    using SafeMath for uint256;

    // ============ Constants ============

    uint256 constant BASE = 10**18;

    // ============ Structs ============

    struct D256 {
        uint256 value;
    }

    // ============ Functions ============

    function zero()
        internal
        pure
        returns (D256 memory)
    {
        return D256({ value: 0 });
    }

    function one()
        internal
        pure
        returns (D256 memory)
    {
        return D256({ value: BASE });
    }

    function onePlus(
        D256 memory d
    )
        internal
        pure
        returns (D256 memory)
    {
        return D256({ value: d.value.add(BASE) });
    }

    function mul(
        uint256 target,
        D256 memory d
    )
        internal
        pure
        returns (uint256)
    {
        return DolomiteMarginMath.getPartial(target, d.value, BASE);
    }

    function div(
        uint256 target,
        D256 memory d
    )
        internal
        pure
        returns (uint256)
    {
        return DolomiteMarginMath.getPartial(target, BASE, d.value);
    }
}

File 22 of 50 : Cache.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Bits } from "./Bits.sol";
import { Interest } from "./Interest.sol";
import { Monetary } from "./Monetary.sol";
import { Require } from "./Require.sol";


/**
 * @title Cache
 * @author dYdX
 *
 * Library for caching information about markets
 */
library Cache {

    // ============ Constants ============

    bytes32 private constant FILE = "Cache";

    // ============ Structs ============

    struct MarketInfo {
        uint marketId;
        address token;
        bool isClosing;
        uint128 borrowPar;
        uint128 supplyPar;
        Interest.Index index;
        Monetary.Price price;
    }

    struct MarketCache {
        MarketInfo[] markets;
        uint256 counter; // used for iterating through the bitmaps and incrementing
        uint256[] marketBitmaps;
        uint256 marketsLength;
    }

    // ============ Setter Functions ============

    /**
     * Initialize an empty cache for some given number of total markets.
     */
    function create(
        uint256 numMarkets
    )
        internal
        pure
        returns (MarketCache memory)
    {
        return MarketCache({
            markets: new MarketInfo[](0),
            counter: 0,
            marketBitmaps: Bits.createBitmaps(numMarkets),
            marketsLength: 0
        });
    }

    // ============ Getter Functions ============

    function getNumMarkets(
        MarketCache memory cache
    )
        internal
        pure
        returns (uint256)
    {
        return cache.markets.length;
    }

    function hasMarket(
        MarketCache memory cache,
        uint256 marketId
    )
        internal
        pure
        returns (bool)
    {
        return Bits.hasBit(cache.marketBitmaps, marketId);
    }

    function get(
        MarketCache memory cache,
        uint256 marketId
    )
        internal
        pure
        returns (MarketInfo memory)
    {
        Require.that(
            cache.markets.length != 0,
            FILE,
            "not initialized"
        );
        return _getInternal(
            cache.markets,
            0,
            cache.marketsLength,
            marketId
        );
    }

    function set(
        MarketCache memory cache,
        uint256 marketId
    )
        internal
        pure
    {
        // Devs should not be able to call this function once the `markets` array has been initialized (non-zero length)
        Require.that(
            cache.markets.length == 0,
            FILE,
            "already initialized"
        );

        Bits.setBit(cache.marketBitmaps, marketId);

        cache.marketsLength += 1;
    }

    function getAtIndex(
        MarketCache memory cache,
        uint256 index
    )
        internal
        pure
        returns (MarketInfo memory)
    {
        Require.that(
            index < cache.markets.length,
            FILE,
            "invalid index",
            index,
            cache.markets.length
        );
        return cache.markets[index];
    }

    // ============ Private Functions ============

    function _getInternal(
        MarketInfo[] memory data,
        uint beginInclusive,
        uint endExclusive,
        uint marketId
    ) private pure returns (MarketInfo memory) {
        uint len = endExclusive - beginInclusive;
        // If length equals 0 OR length equals 1 but the item wasn't found, revert
        assert(!(len == 0 || (len == 1 && data[beginInclusive].marketId != marketId)));

        uint mid = beginInclusive + (len >> 1);
        uint midMarketId = data[mid].marketId;
        if (marketId < midMarketId) {
            return _getInternal(
                data,
                beginInclusive,
                mid,
                marketId
            );
        } else if (marketId > midMarketId) {
            return _getInternal(
                data,
                mid + 1,
                endExclusive,
                marketId
            );
        } else {
            return data[mid];
        }
    }

}

File 23 of 50 : Bits.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Require } from "./Require.sol";


/**
 * @title Bits
 * @author Dolomite
 *
 * Library for caching information about markets
 */
library Bits {

    // ============ Constants ============

    uint256 internal constant ONE = 1;
    uint256 internal constant MAX_UINT_BITS = 256;

    // ============ Functions ============

    function createBitmaps(uint256 maxLength) internal pure returns (uint256[] memory) {
        return new uint256[]((maxLength / MAX_UINT_BITS) + ONE);
    }

    function getMarketIdFromBit(
        uint256 index,
        uint256 bit
    ) internal pure returns (uint256) {
        return (MAX_UINT_BITS * index) + bit;
    }

    function setBit(
        uint256[] memory bitmaps,
        uint256 marketId
    ) internal pure {
        uint256 bucketIndex = marketId / MAX_UINT_BITS;
        uint256 indexFromRight = marketId % MAX_UINT_BITS;
        bitmaps[bucketIndex] |= (ONE << indexFromRight);
    }

    function hasBit(
        uint256[] memory bitmaps,
        uint256 marketId
    ) internal pure returns (bool) {
        uint256 bucketIndex = marketId / MAX_UINT_BITS;
        uint256 indexFromRight = marketId % MAX_UINT_BITS;
        uint256 bit = bitmaps[bucketIndex] & (ONE << indexFromRight);
        return bit != 0;
    }

    function unsetBit(
        uint256 bitmap,
        uint256 bit
    ) internal pure returns (uint256) {
        return bitmap & ~(ONE << bit);
    }

    // solium-disable security/no-assign-params
    function getLeastSignificantBit(uint256 x) internal pure returns (uint256) {
        // gas usage peaks at 350 per call

        uint256 lsb = 255;

        if (x & uint128(-1) != 0) {
            lsb -= 128;
        } else {
            x >>= 128;
        }

        if (x & uint64(-1) != 0) {
            lsb -= 64;
        } else {
            x >>= 64;
        }

        if (x & uint32(-1) != 0) {
            lsb -= 32;
        } else {
            x >>= 32;
        }

        if (x & uint16(-1) != 0) {
            lsb -= 16;
        } else {
            x >>= 16;
        }

        if (x & uint8(-1) != 0) {
            lsb -= 8;
        } else {
            x >>= 8;
        }

        if (x & 0xf != 0) {
            lsb -= 4;
        } else {
            x >>= 4;
        }

        if (x & 0x3 != 0) {
            lsb -= 2;
        } else {
            x >>= 2;
            // solium-enable security/no-assign-params
        }

        if (x & 0x1 != 0) {
            lsb -= 1;
        }

        return lsb;
    }
}

File 24 of 50 : Actions.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Account } from "./Account.sol";
import { Types } from "./Types.sol";


/**
 * @title Actions
 * @author dYdX
 *
 * Library that defines and parses valid Actions
 */
library Actions {

    // ============ Constants ============

    bytes32 private constant FILE = "Actions";

    // ============ Enums ============

    enum ActionType {
        Deposit,   // supply tokens
        Withdraw,  // borrow tokens
        Transfer,  // transfer balance between accounts
        Buy,       // buy an amount of some token (externally)
        Sell,      // sell an amount of some token (externally)
        Trade,     // trade tokens against another account
        Liquidate, // liquidate an undercollateralized or expiring account
        Vaporize,  // use excess tokens to zero-out a completely negative account
        Call       // send arbitrary data to an address
    }

    enum AccountLayout {
        OnePrimary,
        TwoPrimary,
        PrimaryAndSecondary
    }

    enum MarketLayout {
        ZeroMarkets,
        OneMarket,
        TwoMarkets
    }

    // ============ Structs ============

    /*
     * Arguments that are passed to DolomiteMargin in an ordered list as part of a single operation.
     * Each ActionArgs has an actionType which specifies which action struct that this data will be
     * parsed into before being processed.
     */
    struct ActionArgs {
        ActionType actionType;
        uint256 accountId;
        Types.AssetAmount amount;
        uint256 primaryMarketId;
        uint256 secondaryMarketId;
        address otherAddress;
        uint256 otherAccountId;
        bytes data;
    }

    // ============ Action Types ============

    /*
     * Moves tokens from an address to DolomiteMargin. Can either repay a borrow or provide additional supply.
     */
    struct DepositArgs {
        Types.AssetAmount amount;
        Account.Info account;
        uint256 market;
        address from;
    }

    /*
     * Moves tokens from DolomiteMargin to another address. Can either borrow tokens or reduce the amount
     * previously supplied.
     */
    struct WithdrawArgs {
        Types.AssetAmount amount;
        Account.Info account;
        uint256 market;
        address to;
    }

    /*
     * Transfers balance between two accounts. The msg.sender must be an operator for both accounts.
     * The amount field applies to accountOne.
     * This action does not require any token movement since the trade is done internally to DolomiteMargin.
     */
    struct TransferArgs {
        Types.AssetAmount amount;
        Account.Info accountOne;
        Account.Info accountTwo;
        uint256 market;
    }

    /*
     * Acquires a certain amount of tokens by spending other tokens. Sends takerMarket tokens to the
     * specified exchangeWrapper contract and expects makerMarket tokens in return. The amount field
     * applies to the makerMarket.
     */
    struct BuyArgs {
        Types.AssetAmount amount;
        Account.Info account;
        uint256 makerMarket;
        uint256 takerMarket;
        address exchangeWrapper;
        bytes orderData;
    }

    /*
     * Spends a certain amount of tokens to acquire other tokens. Sends takerMarket tokens to the
     * specified exchangeWrapper and expects makerMarket tokens in return. The amount field applies
     * to the takerMarket.
     */
    struct SellArgs {
        Types.AssetAmount amount;
        Account.Info account;
        uint256 takerMarket;
        uint256 makerMarket;
        address exchangeWrapper;
        bytes orderData;
    }

    /*
     * Trades balances between two accounts using any external contract that implements the
     * AutoTrader interface. The AutoTrader contract must be an operator for the makerAccount (for
     * which it is trading on-behalf-of). The amount field applies to the makerAccount and the
     * inputMarket. This proposed change to the makerAccount is passed to the AutoTrader which will
     * quote a change for the makerAccount in the outputMarket (or will disallow the trade).
     * This action does not require any token movement since the trade is done internally to DolomiteMargin.
     */
    struct TradeArgs {
        Types.AssetAmount amount;
        bool calculateAmountWithMakerAccount;
        Account.Info takerAccount;
        Account.Info makerAccount;
        uint256 inputMarket;
        uint256 outputMarket;
        address autoTrader;
        bytes tradeData;
    }

    /*
     * Each account must maintain a certain margin-ratio (specified globally). If the account falls
     * below this margin-ratio, it can be liquidated by any other account. This allows anyone else
     * (arbitrageurs) to repay any borrowed asset (owedMarket) of the liquidating account in
     * exchange for any collateral asset (heldMarket) of the liquidAccount. The ratio is determined
     * by the price ratio (given by the oracles) plus a spread (specified globally). Liquidating an
     * account also sets a flag on the account that the account is being liquidated. This allows
     * anyone to continue liquidating the account until there are no more borrows being taken by the
     * liquidating account. Liquidators do not have to liquidate the entire account all at once but
     * can liquidate as much as they choose. The liquidating flag allows liquidators to continue
     * liquidating the account even if it becomes collateralized through partial liquidation or
     * price movement.
     */
    struct LiquidateArgs {
        Types.AssetAmount amount;
        Account.Info solidAccount;
        Account.Info liquidAccount;
        uint256 owedMarket;
        uint256 heldMarket;
    }

    /*
     * Similar to liquidate, but vaporAccounts are accounts that have only negative balances remaining. The arbitrageur
     * pays back the negative asset (owedMarket) of the vaporAccount in exchange for a collateral asset (heldMarket) at
     * a favorable spread. However, since the liquidAccount has no collateral assets, the collateral must come from
     * DolomiteMargin's excess tokens.
     */
    struct VaporizeArgs {
        Types.AssetAmount amount;
        Account.Info solidAccount;
        Account.Info vaporAccount;
        uint256 owedMarket;
        uint256 heldMarket;
    }

    /*
     * Passes arbitrary bytes of data to an external contract that implements the Callee interface.
     * Does not change any asset amounts. This function may be useful for setting certain variables
     * on layer-two contracts for certain accounts without having to make a separate Ethereum
     * transaction for doing so. Also, the second-layer contracts can ensure that the call is coming
     * from an operator of the particular account.
     */
    struct CallArgs {
        Account.Info account;
        address callee;
        bytes data;
    }

    // ============ Helper Functions ============

    function getMarketLayout(
        ActionType actionType
    )
        internal
        pure
        returns (MarketLayout)
    {
        if (
            actionType == Actions.ActionType.Deposit
            || actionType == Actions.ActionType.Withdraw
            || actionType == Actions.ActionType.Transfer
        ) {
            return MarketLayout.OneMarket;
        }
        else if (actionType == Actions.ActionType.Call) {
            return MarketLayout.ZeroMarkets;
        }
        return MarketLayout.TwoMarkets;
    }

    function getAccountLayout(
        ActionType actionType
    )
        internal
        pure
        returns (AccountLayout)
    {
        if (
            actionType == Actions.ActionType.Transfer
            || actionType == Actions.ActionType.Trade
        ) {
            return AccountLayout.TwoPrimary;
        } else if (
            actionType == Actions.ActionType.Liquidate
            || actionType == Actions.ActionType.Vaporize
        ) {
            return AccountLayout.PrimaryAndSecondary;
        }
        return AccountLayout.OnePrimary;
    }

    // ============ Parsing Functions ============

    function parseDepositArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (DepositArgs memory)
    {
        return DepositArgs({
            amount: args.amount,
            account: accounts[args.accountId],
            market: args.primaryMarketId,
            from: args.otherAddress
        });
    }

    function parseWithdrawArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (WithdrawArgs memory)
    {
        return WithdrawArgs({
            amount: args.amount,
            account: accounts[args.accountId],
            market: args.primaryMarketId,
            to: args.otherAddress
        });
    }

    function parseTransferArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (TransferArgs memory)
    {
        return TransferArgs({
            amount: args.amount,
            accountOne: accounts[args.accountId],
            accountTwo: accounts[args.otherAccountId],
            market: args.primaryMarketId
        });
    }

    function parseBuyArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (BuyArgs memory)
    {
        return BuyArgs({
            amount: args.amount,
            account: accounts[args.accountId],
            makerMarket: args.primaryMarketId,
            takerMarket: args.secondaryMarketId,
            exchangeWrapper: args.otherAddress,
            orderData: args.data
        });
    }

    function parseSellArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (SellArgs memory)
    {
        return SellArgs({
            amount: args.amount,
            account: accounts[args.accountId],
            takerMarket: args.primaryMarketId,
            makerMarket: args.secondaryMarketId,
            exchangeWrapper: args.otherAddress,
            orderData: args.data
        });
    }

    function parseTradeArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (TradeArgs memory)
    {
        (bool calculateAmountWithMakerAccount, bytes memory tradeData) = abi.decode(args.data, (bool, bytes));
        return TradeArgs({
            amount: args.amount,
            calculateAmountWithMakerAccount: calculateAmountWithMakerAccount,
            takerAccount: accounts[args.accountId],
            makerAccount: accounts[args.otherAccountId],
            inputMarket: args.primaryMarketId,
            outputMarket: args.secondaryMarketId,
            autoTrader: args.otherAddress,
            tradeData: tradeData
        });
    }

    function parseLiquidateArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (LiquidateArgs memory)
    {
        return LiquidateArgs({
            amount: args.amount,
            solidAccount: accounts[args.accountId],
            liquidAccount: accounts[args.otherAccountId],
            owedMarket: args.primaryMarketId,
            heldMarket: args.secondaryMarketId
        });
    }

    function parseVaporizeArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (VaporizeArgs memory)
    {
        return VaporizeArgs({
            amount: args.amount,
            solidAccount: accounts[args.accountId],
            vaporAccount: accounts[args.otherAccountId],
            owedMarket: args.primaryMarketId,
            heldMarket: args.secondaryMarketId
        });
    }

    function parseCallArgs(
        Account.Info[] memory accounts,
        ActionArgs memory args
    )
        internal
        pure
        returns (CallArgs memory)
    {
        return CallArgs({
            account: accounts[args.accountId],
            callee: args.otherAddress,
            data: args.data
        });
    }
}

File 25 of 50 : Account.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Types } from "./Types.sol";
import { EnumerableSet } from "./EnumerableSet.sol";


/**
 * @title Account
 * @author dYdX
 *
 * Library of structs and functions that represent an account
 */
library Account {
    // ============ Enums ============

    /*
     * Most-recently-cached account status.
     *
     * Normal: Can only be liquidated if the account values are violating the global margin-ratio.
     * Liquid: Can be liquidated no matter the account values.
     *         Can be vaporized if there are no more positive account values.
     * Vapor:  Has only negative (or zeroed) account values. Can be vaporized.
     *
     */
    enum Status {
        Normal,
        Liquid,
        Vapor
    }

    // ============ Structs ============

    // Represents the unique key that specifies an account
    struct Info {
        address owner;  // The address that owns the account
        uint256 number; // A nonce that allows a single address to control many accounts
    }

    // The complete storage for any account
    struct Storage {
        Status status;
        uint32 numberOfMarketsWithDebt;
        EnumerableSet.Set marketsWithNonZeroBalanceSet;
        mapping (uint256 => Types.Par) balances; // Mapping from marketId to principal
    }

    // ============ Library Functions ============

    function equals(
        Info memory a,
        Info memory b
    )
        internal
        pure
        returns (bool)
    {
        return a.owner == b.owner && a.number == b.number;
    }
}

File 26 of 50 : Address.sol
pragma solidity ^0.5.5;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following 
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }

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

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     *
     * _Available since v2.4.0._
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

File 27 of 50 : IOracleSentinel.sol
/*

    Copyright 2023 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;


/**
 * @title IOracleSentinel
 * @author Dolomite
 *
 * Interface that Dolomite pings to check if the Blockchain or L2 is alive, if liquidations should be processed, and if
 * markets should are in size-down only mode.
 */
contract IOracleSentinel {

    // ============ Events ============

    event GracePeriodSet(
        uint256 gracePeriod
    );

    // ============ Functions ============

    /**
     * @dev Allows the owner to set the grace period duration, which specifies how long the system will disallow
     *      liquidations after sequencer is back online. Only callable by the owner.
     *
     * @param _gracePeriod  The new duration of the grace period
     */
    function ownerSetGracePeriod(
        uint256 _gracePeriod
    )
        external;

    /**
     * @return True if new borrows should be allowed, false otherwise
     */
    function isBorrowAllowed() external view returns (bool);

    /**
     * @return True if liquidations should be allowed, false otherwise
     */
    function isLiquidationAllowed() external view returns (bool);

    /**
     * @return  The duration between when the feed comes back online and when the system will allow liquidations to be
     *          processed normally
     */
    function gracePeriod() external view returns (uint256);
}

File 28 of 50 : IInterestSetter.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Interest } from "../lib/Interest.sol";


/**
 * @title IInterestSetter
 * @author dYdX
 *
 * Interface that Interest Setters for DolomiteMargin must implement in order to report interest rates.
 */
interface IInterestSetter {

    // ============ Public Functions ============

    /**
     * Get the interest rate of a token given some borrowed and supplied amounts
     *
     * @param  token        The address of the ERC20 token for the market
     * @param  borrowWei    The total borrowed token amount for the market
     * @param  supplyWei    The total supplied token amount for the market
     * @return              The interest rate per second
     */
    function getInterestRate(
        address token,
        uint256 borrowWei,
        uint256 supplyWei
    )
        external
        view
        returns (Interest.Rate memory);
}

File 29 of 50 : IExternalCallback.sol
/*

    Copyright 2021 Dolomite.

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

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

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

*/

pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;

import { Account } from "../lib/Account.sol";
import { Types } from "../lib/Types.sol";


/**
 * @title IExternalCallback
 * @author Dolomite
 *
 * Interface that smart contract users can implement to be notified of their balance(s) changing.
 */
interface IExternalCallback {

    /**
     * A callback function to notify the smart contract user that their balance is changing. This function is called
     * after the new balances are set in state, so calling `getAccountPar/Wei` will return each account's balance
     * after `primaryDeltaWei` and `secondaryDeltaWei` are applied.
     *
     * The `_accountOwner` isn't a parameter because technically it is the implementing contract. So, to construct the
     * `Account.Info` object for the primary account, use the following in the implementation:
     * `Account.Info({ owner: address(this), number: _accountNumber })`.
     *
     * @param _primaryAccountNumber The account number of the account being operated on
     * @param _secondaryAccount     The account that is receiving the primary account's assets. To calculate the value
     *                              received by the secondary account, negate primary/secondary delta wei.
     * @param _primaryMarketId      The market that was positive for this account, whose collateral is being seized
     * @param _primaryDeltaWei      The amount of primary market that was received or paid
     * @param _secondaryMarketId    The borrowed balance that is being forcefully repaid
     * @param _secondaryDeltaWei    The amount of borrowed assets to be repaid. Always 0 or positive, since the user's
     *                          balance is going from negative to 0.
     */
    function onInternalBalanceChange(
        uint256 _primaryAccountNumber,
        Account.Info calldata _secondaryAccount,
        uint256 _primaryMarketId,
        Types.Wei calldata _primaryDeltaWei,
        uint256 _secondaryMarketId,
        Types.Wei calldata _secondaryDeltaWei
    ) external;
}

File 30 of 50 : IExchangeWrapper.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;


/**
 * @title IExchangeWrapper
 * @author dYdX
 *
 * Interface that Exchange Wrappers for DolomiteMargin must implement in order to trade ERC20 tokens.
 */
interface IExchangeWrapper {

    // ============ Public Functions ============

    /**
     * Exchange some amount of takerToken for makerToken.
     *
     * @param  _tradeOriginator     Address of the initiator of the trade (however, this value
     *                              cannot always be trusted as it is set at the discretion of the
     *                              msg.sender)
     * @param  _receiver            Address to set allowance on once the trade has completed
     * @param  _makerToken          The token to receive (target asset; IE path[path.length - 1])
     * @param  _takerToken          The token to pay (originator asset; IE path[0])
     * @param  _requestedFillAmount Amount of takerToken being paid
     * @param  _orderData           Arbitrary bytes data for any information to pass to the exchange
     * @return                      The amount of makerToken received
     */
    function exchange(
        address _tradeOriginator,
        address _receiver,
        address _makerToken,
        address _takerToken,
        uint256 _requestedFillAmount,
        bytes calldata _orderData
    )
        external
        returns (uint256);

    /**
     * Get amount of takerToken required to buy a certain amount of makerToken for a given trade.
     * Should match the takerToken amount used in exchangeForAmount. If the order cannot provide
     * exactly desiredMakerToken, then it must return the price to buy the minimum amount greater
     * than desiredMakerToken
     *
     * @param  _makerToken          The token to receive (target asset; IE path[path.length - 1])
     * @param  _takerToken          The token to pay (originator asset; IE path[0])
     * @param  _desiredMakerToken   Amount of `_makerToken` requested
     * @param  _orderData           Arbitrary bytes data for any information to pass to the exchange
     * @return                      Amount of `_takerToken` the needed to complete the exchange
     */
    function getExchangeCost(
        address _makerToken,
        address _takerToken,
        uint256 _desiredMakerToken,
        bytes calldata _orderData
    )
        external
        view
        returns (uint256);
}

File 31 of 50 : IERC20Detailed.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

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


/**
 * @title IERC20
 * @author dYdX
 *
 * Interface for using ERC20 Tokens. We have to use a special interface to call ERC20 functions so
 * that we don't automatically revert when calling non-compliant tokens that have no return value for
 * transfer(), transferFrom(), or approve().
 */
contract IERC20Detailed is IERC20 {

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);
}

File 32 of 50 : IDolomiteMargin.sol
/*

    Copyright 2021 Dolomite.

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

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

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

*/

pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;

import { IAccountRiskOverrideSetter } from "../interfaces/IAccountRiskOverrideSetter.sol";
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "../interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";

import { Account } from "../lib/Account.sol";
import { Actions } from "../lib/Actions.sol";
import { Decimal } from "../lib/Decimal.sol";
import { Interest } from "../lib/Interest.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";

interface IDolomiteMargin {

    // ============ Getters for Risk Params ============

    /**
     * Get the global minimum margin-ratio that every position must maintain to prevent being
     * liquidated.
     *
     * @return  The global margin-ratio
     */
    function getMarginRatio() external view returns (Decimal.D256 memory);

    /**
     * Get the global minimum margin-ratio that every position must maintain to prevent being
     * liquidated.
     *
     * @param account       The account whose margin ratio is being queried. This is used to determine if there is an
     *                      override that supersedes the global minimum.
     * @return  The margin ratio for this account
     */
    function getMarginRatioForAccount(Account.Info calldata account) external view returns (Decimal.D256 memory);

    /**
     * Get the global liquidation spread. This is the spread between oracle prices that incentivizes
     * the liquidation of risky positions.
     *
     * @return  The global liquidation spread
     */
    function getLiquidationSpread() external view returns (Decimal.D256 memory);

    /**
     * Get the adjusted liquidation spread for some market pair. This is equal to the global liquidation spread
     * multiplied by (1 + spreadPremium) for each of the two markets.
     *
     * Assumes the pair is not in e-mode. Backwards compatible with V1.
     *
     * @param  heldMarketId     The market for which the account has collateral
     * @param  owedMarketId     The market for which the account has borrowed tokens
     * @return                  The adjusted liquidation spread
     */
    function getLiquidationSpreadForPair(
        uint256 heldMarketId,
        uint256 owedMarketId
    ) external view returns (Decimal.D256 memory);

    /**
     * Get the adjusted liquidation spread for some market pair. This is equal to the global liquidation spread
     * multiplied by (1 + spreadPremium) for each of the two markets.
     *
     * If the pair is in e-mode and has a liquidation spread override, then the override is used instead.
     *
     * @param  account      The account whose liquidation spread is being queried. This is used to determine if there is
     *                      an override in place.
     * @param  heldMarketId The market for which the account has collateral
     * @param  owedMarketId The market for which the account has borrowed tokens
     * @return              The adjusted liquidation spread
     */
    function getLiquidationSpreadForAccountAndPair(
        Account.Info calldata account,
        uint256 heldMarketId,
        uint256 owedMarketId
    ) external view returns (Decimal.D256 memory);

    /**
     * Get the global earnings-rate variable that determines what percentage of the interest paid
     * by borrowers gets passed-on to suppliers.
     *
     * @return  The global earnings rate
     */
    function getEarningsRate() external view returns (Decimal.D256 memory);

    /**
     * Get the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
     *
     * @return  The global minimum borrow value
     */
    function getMinBorrowedValue() external view returns (Monetary.Value memory);

    /**
     * Get the maximum number of assets an account owner can hold in an account number.
     *
     * @return  The maximum number of assets an account owner can hold in an account number.
     */
    function getAccountMaxNumberOfMarketsWithBalances() external view returns (uint256);

    /**
     * Gets the oracle sentinel, which is responsible for checking if the Blockchain or L2 is alive, if liquidations
     * should be processed, and if markets should are in size-down only mode.
     *
     * @return The oracle sentinel for DolomiteMargin
     */
    function getOracleSentinel() external view returns (IOracleSentinel);

    /**
     * @return True if borrowing is globally allowed according to the Oracle Sentinel or false if it is not
     */
    function getIsBorrowAllowed() external view returns (bool);

    /**
     * @return True if liquidations are globally allowed according to the Oracle Sentinel or false if they are not
     */
    function getIsLiquidationAllowed() external view returns (bool);

    /**
     * @return  The gas limit used for making callbacks via `IExternalCallback::onInternalBalanceChange` to smart
     *          contract wallets.
     */
    function getCallbackGasLimit() external view returns (uint256);

    /**
     * Get the account risk override getter for global use. This contract enables e-mode based on the assets held in a
     * position.
     *
     * @return  The contract that contains risk override information for any account that does NOT have an account-
     *          specific override.
     */
    function getDefaultAccountRiskOverrideSetter() external view returns (IAccountRiskOverrideSetter);

    /**
     * Get the account risk override getter for an account owner. This contract enables e-mode for certain isolation
     * mode vaults.
     *
     * @param accountOwner  The address of the account to check if there is a margin ratio override.
     * @return  The contract that contains risk override information for this account.
     */
    function getAccountRiskOverrideSetterByAccountOwner(
        address accountOwner
    ) external view returns (IAccountRiskOverrideSetter);

    /**
     * Get the margin ratio override for an account owner. Used to enable e-mode for certain isolation mode vaults.
     *
     * @param account                       The account to check if there is a risk override.
     * @return marginRatioOverride          The margin ratio override for an account owner. Defaults to 0 if there's no
     *                                      override in place.
     * @return liquidationSpreadOverride    The margin ratio override for an account owner. Defaults to 0 if there's no
     *                                      override in place.
     */
    function getAccountRiskOverrideByAccount(
        Account.Info calldata account
    )
    external
    view
    returns (Decimal.D256 memory marginRatioOverride, Decimal.D256 memory liquidationSpreadOverride);

    /**
     * Get the margin ratio override for an account. Used to enable e-mode for certain accounts/positions.
     *
     * @param account   The account to check if there is a margin ratio override.
     * @return  The margin ratio override for an account owner. Defaults to 0 if there's no override in place.
     */
    function getMarginRatioOverrideByAccount(Account.Info calldata account) external view returns (Decimal.D256 memory);

    /**
     * Get the liquidation reward override for an account owner. Used to enable e-mode for certain isolation mode
     * vaults.
     *
     * @param account   The account to check if there is a liquidation spread override.
     * @return  The liquidation spread override for an account owner. Defaults to 0 if there's no override in place.
     */
    function getLiquidationSpreadOverrideByAccount(
        Account.Info calldata account
    ) external view returns (Decimal.D256 memory);

    /**
     * Get all risk parameter limits in a single struct. These are the maximum limits at which the
     * risk parameters can be set by the admin of DolomiteMargin.
     *
     * @return  All global risk parameter limits
     */
    function getRiskLimits() external view returns (Storage.RiskLimits memory);

    // ============ Getters for Markets ============

    /**
     * Get the total number of markets.
     *
     * @return  The number of markets
     */
    function getNumMarkets() external view returns (uint256);

    /**
     * Get the ERC20 token address for a market.
     *
     * @param  token    The token to query
     * @return          The token's marketId if the token is valid
     */
    function getMarketIdByTokenAddress(
        address token
    ) external view returns (uint256);

    /**
     * Get the ERC20 token address for a market.
     *
     * @param  marketId  The market to query
     * @return           The token address
     */
    function getMarketTokenAddress(
        uint256 marketId
    ) external view returns (address);

    /**
     * Return true if a particular market is in closing mode. Additional borrows cannot be taken
     * from a market that is closing.
     *
     * @param  marketId  The market to query
     * @return           True if the market is closing
     */
    function getMarketIsClosing(
        uint256 marketId
    )
    external
    view
    returns (bool);

    /**
     * Get the price of the token for a market.
     *
     * @param  marketId  The market to query
     * @return           The price of each atomic unit of the token
     */
    function getMarketPrice(
        uint256 marketId
    ) external view returns (Monetary.Price memory);

    /**
     * Get the total principal amounts (borrowed and supplied) for a market.
     *
     * @param  marketId  The market to query
     * @return           The total principal amounts
     */
    function getMarketTotalPar(
        uint256 marketId
    ) external view returns (Types.TotalPar memory);

    /**
     * Get the total principal amounts (borrowed and supplied) for a market.
     *
     * @param  marketId  The market to query
     * @return           The total principal amounts
     */
    function getMarketTotalWei(
        uint256 marketId
    ) external view returns (Types.TotalWei memory);

    /**
     * Get the most recently cached interest index for a market.
     *
     * @param  marketId  The market to query
     * @return           The most recent index
     */
    function getMarketCachedIndex(
        uint256 marketId
    ) external view returns (Interest.Index memory);

    /**
     * Get the interest index for a market if it were to be updated right now.
     *
     * @param  marketId  The market to query
     * @return           The estimated current index
     */
    function getMarketCurrentIndex(
        uint256 marketId
    ) external view returns (Interest.Index memory);

    /**
     * Get the price oracle address for a market.
     *
     * @param  marketId  The market to query
     * @return           The price oracle address
     */
    function getMarketPriceOracle(
        uint256 marketId
    ) external view returns (IPriceOracle);

    /**
     * Get the interest-setter address for a market.
     *
     * @param  marketId  The market to query
     * @return           The interest-setter address
     */
    function getMarketInterestSetter(
        uint256 marketId
    ) external view returns (IInterestSetter);

    /**
     * Get the margin premium for a market. A margin premium makes it so that any positions that
     * include the market require a higher collateralization to avoid being liquidated.
     *
     * @param  marketId  The market to query
     * @return           The market's margin premium
     */
    function getMarketMarginPremium(
        uint256 marketId
    ) external view returns (Decimal.D256 memory);

    /**
     * Get the spread premium for a market. A spread premium makes it so that any liquidations
     * that include the market have a higher spread than the global default.
     *
     * @param  marketId  The market to query
     * @return           The market's spread premium
     */
    function getMarketLiquidationSpreadPremium(
        uint256 marketId
    ) external view returns (Decimal.D256 memory);

    /**
     * Same as getMarketLiquidationSpreadPremium. Added for backwards-compatibility.
     *
     * @param  marketId  The market to query
     * @return           The market's spread premium
     */
    function getMarketSpreadPremium(
        uint256 marketId
    ) external view returns (Decimal.D256 memory);

    /**
     * Same as getMarketMaxSupplyWei. Added for backwards-compatibility.
     *
     * @param  marketId  The market to query
     * @return           The max amount of the market that can be supplied. Always 0 or positive.
     */
    function getMarketMaxWei(
        uint256 marketId
    )
    external
    view
    returns (Types.Wei memory);

    /**
     * Get the max supply amount for a a market.
     *
     * @param  marketId  The market to query
     * @return           The market's max supply amount. Always 0 or positive.
     */
    function getMarketMaxSupplyWei(
        uint256 marketId
    )
    external
    view
    returns (Types.Wei memory);

    /**
     * Get the max borrow amount for a a market.
     *
     * @param  marketId  The market to query
     * @return           The market's max borrow amount. Always negative or 0.
     */
    function getMarketMaxBorrowWei(
        uint256 marketId
    )
    external
    view
    returns (Types.Wei memory);

    /**
     * Get the market-specific earnings that determines what percentage of the interest paid by borrowers gets passed-on
     * to suppliers. If the value is set to 0, the override is not set.
     *
     * @return  The market-specific earnings rate
     */
    function getMarketEarningsRateOverride(
        uint256 marketId
    )
    external
    view
    returns (Decimal.D256 memory);

    /**
     * Get the current borrow interest rate for a market. The value is denominated as interest paid per second, and the
     * number is scaled to have 18 decimals. To get APR, multiply the number returned by 31536000 (seconds in a year).
     *
     * @param  marketId  The market to query
     * @return           The current borrow interest rate
     */
    function getMarketBorrowInterestRatePerSecond(
        uint256 marketId
    ) external view returns (Interest.Rate memory);

    /**
     * Same as getMarketBorrowInterestRatePerSecond. Added for backwards-compatibility.
     *
     * @param  marketId  The market to query
     * @return           The current borrow interest rate
     */
    function getMarketInterestRate(
        uint256 marketId
    ) external view returns (Interest.Rate memory);

    /**
     * Get the current borrow interest rate for a market. The value is denominated as interest paid per year, and the
     * number is scaled to have 18 decimals.
     *
     * @param  marketId  The market to query
     * @return           The current supply interest rate
     */
    function getMarketBorrowInterestRateApr(
        uint256 marketId
    ) external view returns (Interest.Rate memory);

    /**
     * Get the current supply interest rate for a market.
     *
     * @param  marketId  The market to query
     * @return           The current supply interest rate
     */
    function getMarketSupplyInterestRateApr(
        uint256 marketId
    ) external view returns (Interest.Rate memory);

    /**
     * Get basic information about a particular market.
     *
     * @param  marketId  The market to query
     * @return           A Storage.Market struct with the current state of the market
     */
    function getMarket(
        uint256 marketId
    ) external view returns (Storage.Market memory);

    /**
     * Get comprehensive information about a particular market.
     *
     * @param  marketId  The market to query
     * @return           A tuple containing the values:
     *                    - A Storage.Market struct with the current state of the market
     *                    - The current estimated interest index
     *                    - The current token price
     *                    - The current market borrow interest rate per second
     */
    function getMarketWithInfo(
        uint256 marketId
    )
    external
    view
    returns (
        Storage.Market memory,
        Interest.Index memory,
        Monetary.Price memory,
        Interest.Rate memory
    );

    /**
     * Get the number of tokens that are owed to the `owner` of DolomiteMargin. The number of excess tokens is
     * calculated by taking the current number of tokens held in DolomiteMargin, adding the number of tokens owed to
     * DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by DolomiteMargin.
     *
     * @param  marketId  The market to query
     * @return           The number of excess tokens
     */
    function getNumExcessTokens(
        uint256 marketId
    ) external view returns (Types.Wei memory);

    // ============ Getters for Accounts ============

    /**
     * Get the principal value for a particular account and market.
     *
     * @param  account   The account to query
     * @param  marketId  The market to query
     * @return           The principal value
     */
    function getAccountPar(
        Account.Info calldata account,
        uint256 marketId
    ) external view returns (Types.Par memory);

    /**
     * Get the token balance for a particular account and market.
     *
     * @param  account   The account to query
     * @param  marketId  The market to query
     * @return           The token amount
     */
    function getAccountWei(
        Account.Info calldata account,
        uint256 marketId
    ) external view returns (Types.Wei memory);

    /**
     * Get the status of an account (Normal, Liquidating, or Vaporizing).
     *
     * @param  account  The account to query
     * @return          The account's status
     */
    function getAccountStatus(
        Account.Info calldata account
    ) external view returns (Account.Status);

    /**
     * Get a list of markets that have a non-zero balance for an account
     *
     * @param  account  The account to query
     * @return          The non-sorted marketIds with non-zero balance for the account.
     */
    function getAccountMarketsWithBalances(
        Account.Info calldata account
    ) external view returns (uint256[] memory);

    /**
     * Get the number of markets that have a non-zero balance for an account
     *
     * @param  account  The account to query
     * @return          The number of markets with a non-zero balance for the account.
     */
    function getAccountNumberOfMarketsWithBalances(
        Account.Info calldata account
    ) external view returns (uint256);

    /**
     * Get the marketId for an account's market with a non-zero balance at the given index
     *
     * @param  account  The account to query
     * @return          The market ID in the provided index for the account.
     */
    function getAccountMarketWithBalanceAtIndex(
        Account.Info calldata account,
        uint256 index
    ) external view returns (uint256);

    /**
     * Get the number of markets with which an account has a negative balance.
     *
     * @param  account  The account to query
     * @return          The number of markets with a negative balance for this account.
     */
    function getAccountNumberOfMarketsWithDebt(
        Account.Info calldata account
    ) external view returns (uint256);

    /**
     * Get the total supplied and total borrowed value of an account.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The supplied value of the account
     *                   - The borrowed value of the account
     */
    function getAccountValues(
        Account.Info calldata account
    ) external view returns (Monetary.Value memory, Monetary.Value memory);

    /**
     * Get the total supplied and total borrowed values of an account adjusted by the marginPremium
     * of each market. Supplied values are divided by (1 + marginPremium) for each market and
     * borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
     * adjusted values gives the margin-ratio of the account which will be compared to the global
     * margin-ratio when determining if the account can be liquidated.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The supplied value of the account (adjusted for marginPremium)
     *                   - The borrowed value of the account (adjusted for marginPremium)
     */
    function getAdjustedAccountValues(
        Account.Info calldata account
    ) external view returns (Monetary.Value memory, Monetary.Value memory);

    /**
     * Get an account's summary for each market.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The market IDs for each market
     *                   - The ERC20 token address for each market
     *                   - The account's principal value for each market
     *                   - The account's (supplied or borrowed) number of tokens for each market
     */
    function getAccountBalances(
        Account.Info calldata account
    ) external view returns (uint[] memory, address[] memory, Types.Par[] memory, Types.Wei[] memory);

    // ============ Getters for Account Permissions ============

    /**
     * Return true if a particular address is approved as an operator for an owner's accounts.
     * Approved operators can act on the accounts of the owner as if it were the operator's own.
     *
     * @param  owner     The owner of the accounts
     * @param  operator  The possible operator
     * @return           True if operator is approved for owner's accounts
     */
    function getIsLocalOperator(
        address owner,
        address operator
    ) external view returns (bool);

    /**
     * Return true if a particular address is approved as a global operator. Such an address can
     * act on any account as if it were the operator's own.
     *
     * @param  operator  The address to query
     * @return           True if operator is a global operator
     */
    function getIsGlobalOperator(
        address operator
    ) external view returns (bool);

    /**
     * Checks if the autoTrader can only be called invoked by a global operator
     *
     * @param autoTrader    The trader that should be checked for special call privileges.
     */
    function getIsAutoTraderSpecial(address autoTrader) external view returns (bool);

    // ============ Write Functions ============

    /**
     * The main entry-point to DolomiteMargin that allows users and contracts to manage accounts.
     * Take one or more actions on one or more accounts. The msg.sender must be the owner or
     * operator of all accounts except for those being liquidated, vaporized, or traded with.
     * One call to operate() is considered a singular "operation". Account collateralization is
     * ensured only after the completion of the entire operation.
     *
     * @param  accounts  A list of all accounts that will be used in this operation. Cannot contain
     *                   duplicates. In each action, the relevant account will be referred-to by its
     *                   index in the list.
     * @param  actions   An ordered list of all actions that will be taken in this operation. The
     *                   actions will be processed in order.
     */
    function operate(
        Account.Info[] calldata accounts,
        Actions.ActionArgs[] calldata actions
    ) external;

    /**
     * Approves/disapproves any number of operators. An operator is an external address that has the
     * same permissions to manipulate an account as the owner of the account. Operators are simply
     * addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
     *
     * Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
     * operator is a smart contract and implements the IAutoTrader interface.
     *
     * @param  args  A list of OperatorArgs which have an address and a boolean. The boolean value
     *               denotes whether to approve (true) or revoke approval (false) for that address.
     */
    function setOperators(
        Types.OperatorArg[] calldata args
    ) external;

    // =========================================
    // ============ Owner Functions ============
    // =========================================

    // ============ Token Functions ============

    /**
     * Withdraw an ERC20 token for which there is an associated market. Only excess tokens can be withdrawn. The number
     * of excess tokens is calculated by taking the current number of tokens held in DolomiteMargin, adding the number
     * of tokens owed to DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by
     * DolomiteMargin.
     */
    function ownerWithdrawExcessTokens(
        uint256 marketId,
        address recipient
    )
    external
    returns (uint256);

    /**
     * Withdraw an ERC20 token for which there is no associated market.
     */
    function ownerWithdrawUnsupportedTokens(
        address token,
        address recipient
    )
    external
    returns (uint256);

    // ============ Market Functions ============

    /**
     * Add a new market to DolomiteMargin. Must be for a previously-unsupported ERC20 token.
     */
    function ownerAddMarket(
        address token,
        IPriceOracle priceOracle,
        IInterestSetter interestSetter,
        Decimal.D256 calldata marginPremium,
        Decimal.D256 calldata spreadPremium,
        uint256 maxSupplyWei,
        uint256 maxBorrowWei,
        Decimal.D256 calldata earningsRateOverride,
        bool isClosing
    )
    external;

    /**
     * Set (or unset) the status of a market to "closing". The borrowedValue of a market cannot increase while its
     * status is "closing".
     */
    function ownerSetIsClosing(
        uint256 marketId,
        bool isClosing
    )
    external;

    /**
     * Set the price oracle for a market.
     */
    function ownerSetPriceOracle(
        uint256 marketId,
        IPriceOracle priceOracle
    )
    external;

    /**
     * Set the interest-setter for a market.
     */
    function ownerSetInterestSetter(
        uint256 marketId,
        IInterestSetter interestSetter
    )
    external;

    /**
     * Set a premium on the minimum margin-ratio for a market. This makes it so that any positions that include this
     * market require a higher collateralization to avoid being liquidated.
     */
    function ownerSetMarginPremium(
        uint256 marketId,
        Decimal.D256 calldata marginPremium
    )
    external;

    /**
     * Set a premium on the liquidation spread for a market. This makes it so that any liquidations that include this
     * market have a higher spread than the global default.
     */
    function ownerSetLiquidationSpreadPremium(
        uint256 marketId,
        Decimal.D256 calldata liquidationSpreadPremium
    )
    external;

    /**
     * Sets the maximum supply wei for a given `marketId`.
     */
    function ownerSetMaxSupplyWei(
        uint256 marketId,
        uint256 maxSupplyWei
    )
    external;

    /**
     * Sets the maximum borrow wei for a given `marketId`.
     */
    function ownerSetMaxBorrowWei(
        uint256 marketId,
        uint256 maxBorrowWei
    )
    external;

    /**
     * Sets the earnings rate override for a given `marketId`. Set it to 0 unset the override.
     */
    function ownerSetEarningsRateOverride(
        uint256 marketId,
        Decimal.D256 calldata earningsRateOverride
    )
    external;

    // ============ Risk Functions ============

    /**
     * Set the global minimum margin-ratio that every position must maintain to prevent being liquidated.
     */
    function ownerSetMarginRatio(
        Decimal.D256 calldata ratio
    )
    external;

    /**
     * Set the global liquidation spread. This is the spread between oracle prices that incentivizes the liquidation of
     * risky positions.
     */
    function ownerSetLiquidationSpread(
        Decimal.D256 calldata spread
    )
    external;

    /**
     * Set the global earnings-rate variable that determines what percentage of the interest paid by borrowers gets
     * passed-on to suppliers.
     */
    function ownerSetEarningsRate(
        Decimal.D256 calldata earningsRate
    )
    external;

    /**
     * Set the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
     */
    function ownerSetMinBorrowedValue(
        Monetary.Value calldata minBorrowedValue
    )
    external;

    /**
     * Sets the number of non-zero balances an account may have within the same `accountIndex`. This ensures a user
     * cannot DOS the system by filling their account with non-zero balances (which linearly increases gas costs when
     * checking collateralization) and disallowing themselves to close the position, because the number of gas units
     * needed to process their transaction exceed the block's gas limit. In turn, this would  prevent the user from also
     * being liquidated, causing the all of the capital to be "stuck" in the position.
     *
     * Lowering this number does not "freeze" user accounts that have more than the new limit of balances, because this
     * variable is enforced by checking the users number of non-zero balances against the max or if it sizes down before
     * each transaction finishes.
     */
    function ownerSetAccountMaxNumberOfMarketsWithBalances(
        uint256 accountMaxNumberOfMarketsWithBalances
    )
    external;

    /**
     * Sets the current oracle sentinel used to report if borrowing and liquidations are enabled.
     */
    function ownerSetOracleSentinel(
        IOracleSentinel oracleSentinel
    )
    external;

    /**
     * Sets the gas limit that's passed to any of the callback functions
     */
    function ownerSetCallbackGasLimit(
        uint256 callbackGasLimit
    )
    external;

    /**
     * Sets the account risk override setter by default for any account
     */
    function ownerSetDefaultAccountRiskOverride(
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    )
    external;

    /**
     * Sets the account risk override setter for a given wallet
     */
    function ownerSetAccountRiskOverride(
        address accountOwner,
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    )
    external;

    // ============ Global Operator Functions ============

    /**
     * Approve (or disapprove) an address that is permissioned to be an operator for all accounts in DolomiteMargin.
     * Intended only to approve smart-contracts.
     */
    function ownerSetGlobalOperator(
        address operator,
        bool approved
    )
    external;

    /**
     * Approve (or disapprove) an auto trader that can only be called by a global operator. IE for expirations
     */
    function ownerSetAutoTraderSpecial(
        address autoTrader,
        bool special
    )
    external;

    // ============ Owner Functions ============

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() external view returns (address);

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() external view returns (bool);

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

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

File 33 of 50 : ICallee.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Account } from "../lib/Account.sol";


/**
 * @title ICallee
 * @author dYdX
 *
 * Interface that Callees for DolomiteMargin must implement in order to ingest data.
 */
contract ICallee {

    // ============ Public Functions ============

    /**
     * Allows users to send this contract arbitrary data.
     *
     * @param  sender       The msg.sender to DolomiteMargin
     * @param  accountInfo  The account from which the data is being sent
     * @param  data         Arbitrary data given by the sender
     */
    function callFunction(
        address sender,
        Account.Info memory accountInfo,
        bytes memory data
    )
        public;
}

File 34 of 50 : IAutoTrader.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Account } from "../lib/Account.sol";
import { Types } from "../lib/Types.sol";


/**
 * @title IAutoTrader
 * @author dYdX
 *
 * Interface that Auto-Traders for DolomiteMargin must implement in order to approve trades.
 */
contract IAutoTrader {

    // ============ Public Functions ============

    /**
     * Allows traders to make trades approved by this smart contract. The active trader's account is
     * the takerAccount and the passive account (for which this contract approves trades
     * on-behalf-of) is the makerAccount.
     *
     * @param  inputMarketId   The market for which the trader specified the original amount
     * @param  outputMarketId  The market for which the trader wants the resulting amount specified
     * @param  makerAccount    The account for which this contract is making trades
     * @param  takerAccount    The account requesting the trade
     * @param  oldInputPar     The old principal amount for the makerAccount for the inputMarketId
     * @param  newInputPar     The new principal amount for the makerAccount for the inputMarketId
     * @param  inputDeltaWei   The change in token amount for the makerAccount for the inputMarketId
     * @param  data            Arbitrary data passed in by the trader
     * @return                 The AssetAmount for the makerAccount for the outputMarketId
     */
    function getTradeCost(
        uint256 inputMarketId,
        uint256 outputMarketId,
        Account.Info memory makerAccount,
        Account.Info memory takerAccount,
        Types.Par memory oldInputPar,
        Types.Par memory newInputPar,
        Types.Wei memory inputDeltaWei,
        bytes memory data
    )
        public
        returns (Types.AssetAmount memory);
}

File 35 of 50 : IAccountRiskOverrideSetter.sol
/*

    Copyright 2023 Dolomite.

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

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

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

*/

pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;

import { Account } from "../lib/Account.sol";
import { Decimal } from "../lib/Decimal.sol";


/**
 * @title IAccountRiskOverrideSetter
 * @author Dolomite
 *
 * @notice Interface that can be implemented by any contract that needs to implement risk overrides for an account.
 */
interface IAccountRiskOverrideSetter {

    /**
     * @notice  Gets the risk overrides for a given account owner.
     *
     * @param   _account                    The account whose risk override should be retrieved.
     * @return  marginRatioOverride         The margin ratio override for this account.
     * @return  liquidationSpreadOverride   The liquidation spread override for this account.
     */
    function getAccountRiskOverride(
        Account.Info calldata _account
    )
        external
        view
        returns
    (
        Decimal.D256 memory marginRatioOverride,
        Decimal.D256 memory liquidationSpreadOverride
    );
}

File 36 of 50 : WithdrawalImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Actions } from "../lib/Actions.sol";
import { Events } from "../lib/Events.sol";
import { Exchange } from "../lib/Exchange.sol";
import { Interest } from "../lib/Interest.sol";
import { Require } from "../lib/Require.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";


library WithdrawalImpl {
    using Storage for Storage.State;

    // ============ Constants ============

    bytes32 private constant FILE = "WithdrawalImpl";

    // ============ Account Actions ============

    function withdraw(
        Storage.State storage state,
        Actions.WithdrawArgs memory args,
        Interest.Index memory index
    )
    public
    {
        state.requireIsOperator(args.account, msg.sender);

        (
            Types.Par memory newPar,
            Types.Wei memory deltaWei
        ) = state.getNewParAndDeltaWei(
            args.account,
            args.market,
            index,
            args.amount
        );

        state.setPar(
            args.account,
            args.market,
            newPar
        );

        // requires a negative deltaWei
        Exchange.transferOut(
            state.getToken(args.market),
            args.to,
            deltaWei
        );

        Events.logWithdraw(
            state,
            args,
            deltaWei
        );
    }
}

File 37 of 50 : TransferImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Actions } from "../lib/Actions.sol";
import { Events } from "../lib/Events.sol";
import { Interest } from "../lib/Interest.sol";
import { SafeExternalCallback } from "../lib/SafeExternalCallback.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";


library TransferImpl {
    using Storage for Storage.State;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "TransferImpl";

    // ============ Account Actions ============

    function transfer(
        Storage.State storage state,
        Actions.TransferArgs memory args,
        Interest.Index memory index
    )
    public
    {
        state.requireIsOperator(args.accountOne, msg.sender);
        state.requireIsOperator(args.accountTwo, msg.sender);

        (
            Types.Par memory newPar,
            Types.Wei memory deltaWei
        ) = state.getNewParAndDeltaWei(
            args.accountOne,
            args.market,
            index,
            args.amount
        );

        state.setPar(
            args.accountOne,
            args.market,
            newPar
        );

        state.setParFromDeltaWei(
            args.accountTwo,
            args.market,
            index,
            deltaWei.negative()
        );

        uint256 callbackGasLimit = state.riskParams.callbackGasLimit;
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.accountOne,
            args.accountTwo,
            args.market,
            deltaWei,
            /* _secondaryMarketId = */ 0,
            /* _secondaryDeltaWei = */ Types.zeroWei(),
            callbackGasLimit
        );
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.accountTwo,
            args.accountOne,
            args.market,
            deltaWei.negative(),
            /* _secondaryMarketId = */ 0,
            /* _secondaryDeltaWei = */ Types.zeroWei(),
            callbackGasLimit
        );

        Events.logTransfer(
            state,
            args,
            deltaWei
        );
    }
}

File 38 of 50 : TradeImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { IAutoTrader } from "../interfaces/IAutoTrader.sol";

import { Actions } from "../lib/Actions.sol";
import { Cache } from "../lib/Cache.sol";
import { Events } from "../lib/Events.sol";
import { Exchange } from "../lib/Exchange.sol";
import { Interest } from "../lib/Interest.sol";
import { Require } from "../lib/Require.sol";
import { SafeExternalCallback } from "../lib/SafeExternalCallback.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";


library TradeImpl {
    using Storage for Storage.State;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "TradeImpl";

    // ============ Account Actions ============

    function buy(
        Storage.State storage state,
        Actions.BuyArgs memory args,
        Interest.Index memory takerIndex,
        Interest.Index memory makerIndex
    )
    public
    {
        state.requireIsOperator(args.account, msg.sender);

        address takerToken = state.getToken(args.takerMarket);
        address makerToken = state.getToken(args.makerMarket);

        (, Types.Wei memory makerWei) = state.getNewParAndDeltaWei(
            args.account,
            args.makerMarket,
            makerIndex,
            args.amount
        );

        Types.Wei memory takerWei = Exchange.getCost(
            args.exchangeWrapper,
            makerToken,
            takerToken,
            makerWei,
            args.orderData
        );

        Types.Wei memory tokensReceived = Exchange.exchange(
            args.exchangeWrapper,
            args.account.owner,
            makerToken,
            takerToken,
            takerWei,
            args.orderData
        );

        Require.that(
            tokensReceived.value >= makerWei.value,
            FILE,
            "Buy amount less than promised",
            tokensReceived.value
        );

        state.setParFromDeltaWei(
            args.account,
            args.makerMarket,
            makerIndex,
            tokensReceived
        );

        state.setParFromDeltaWei(
            args.account,
            args.takerMarket,
            takerIndex,
            takerWei
        );

        Events.logBuy(
            state,
            args,
            takerWei,
            makerWei
        );
    }

    function sell(
        Storage.State storage state,
        Actions.SellArgs memory args,
        Interest.Index memory takerIndex,
        Interest.Index memory makerIndex
    )
    public
    {
        state.requireIsOperator(args.account, msg.sender);

        address takerToken = state.getToken(args.takerMarket);
        address makerToken = state.getToken(args.makerMarket);

        (
            Types.Par memory takerPar,
            Types.Wei memory takerWei
        ) = state.getNewParAndDeltaWei(
            args.account,
            args.takerMarket,
            takerIndex,
            args.amount
        );

        Types.Wei memory makerWei = Exchange.exchange(
            args.exchangeWrapper,
            args.account.owner,
            makerToken,
            takerToken,
            takerWei,
            args.orderData
        );

        state.setPar(
            args.account,
            args.takerMarket,
            takerPar
        );

        state.setParFromDeltaWei(
            args.account,
            args.makerMarket,
            makerIndex,
            makerWei
        );

        Events.logSell(
            state,
            args,
            takerWei,
            makerWei
        );
    }

    function trade(
        Storage.State storage state,
        Actions.TradeArgs memory args,
        Interest.Index memory inputIndex,
        Interest.Index memory outputIndex
    )
    public
    {
        state.requireIsOperator(args.takerAccount, msg.sender);
        state.requireIsOperator(args.makerAccount, args.autoTrader);
        if (state.isAutoTraderSpecial(args.autoTrader)) {
            Require.that(
                state.isGlobalOperator(msg.sender),
                FILE,
                "Unpermissioned trade operator"
            );
        }

        Types.Par memory oldInputPar = state.getPar(
            args.makerAccount,
            args.inputMarket
        );

        Types.Par memory newInputPar;
        Types.Wei memory inputWei;
        if (args.calculateAmountWithMakerAccount) {
            (newInputPar, inputWei) = state.getNewParAndDeltaWei(
                args.makerAccount,
                args.inputMarket,
                inputIndex,
                args.amount
            );
        } else {
            (,Types.Wei memory takerInputWei) = state.getNewParAndDeltaWei(
                args.takerAccount,
                args.inputMarket,
                inputIndex,
                args.amount
            );
            // invert the sign for the maker account
            Types.AssetAmount memory makerAssetAmount = Types.AssetAmount({
                sign: !takerInputWei.sign,
                denomination: Types.AssetDenomination.Wei,
                ref: Types.AssetReference.Delta,
                value: takerInputWei.value
            });
            (newInputPar, inputWei) = state.getNewParAndDeltaWei(
                args.makerAccount,
                args.inputMarket,
                inputIndex,
                makerAssetAmount
            );
        }

        Types.AssetAmount memory outputAmount = IAutoTrader(args.autoTrader).getTradeCost(
            args.inputMarket,
            args.outputMarket,
            args.makerAccount,
            args.takerAccount,
            oldInputPar,
            newInputPar,
            inputWei,
            args.tradeData
        );

        (
            Types.Par memory newOutputPar,
            Types.Wei memory outputWei
        ) = state.getNewParAndDeltaWei(
            args.makerAccount,
            args.outputMarket,
            outputIndex,
            outputAmount
        );

        Require.that(
            outputWei.isZero() || inputWei.isZero() || outputWei.sign != inputWei.sign,
            FILE,
            "Trades cannot be one-sided",
            args.autoTrader
        );

        // set the balance for the maker
        state.setPar(
            args.makerAccount,
            args.inputMarket,
            newInputPar
        );
        state.setPar(
            args.makerAccount,
            args.outputMarket,
            newOutputPar
        );

        // set the balance for the taker
        state.setParFromDeltaWei(
            args.takerAccount,
            args.inputMarket,
            inputIndex,
            inputWei.negative()
        );
        state.setParFromDeltaWei(
            args.takerAccount,
            args.outputMarket,
            outputIndex,
            outputWei.negative()
        );

        uint256 callbackGasLimit = state.riskParams.callbackGasLimit;
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.makerAccount,
            args.takerAccount,
            args.inputMarket,
            inputWei,
            args.outputMarket,
            outputWei,
            callbackGasLimit
        );
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.takerAccount,
            args.makerAccount,
            args.inputMarket,
            inputWei.negative(),
            args.outputMarket,
            outputWei.negative(),
            callbackGasLimit
        );

        Events.logTrade(
            state,
            args,
            inputWei,
            outputWei
        );
    }
}

File 39 of 50 : OperationImpl.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IAutoTrader } from "../interfaces/IAutoTrader.sol";
import { ICallee } from "../interfaces/ICallee.sol";
import { Account } from "../lib/Account.sol";
import { Actions } from "../lib/Actions.sol";
import { Cache } from "../lib/Cache.sol";
import { Decimal } from "../lib/Decimal.sol";
import { EnumerableSet } from "../lib/EnumerableSet.sol";
import { Events } from "../lib/Events.sol";
import { Exchange } from "../lib/Exchange.sol";
import { Interest } from "../lib/Interest.sol";
import { DolomiteMarginMath } from "../lib/DolomiteMarginMath.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Require } from "../lib/Require.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";
import { CallImpl } from "./CallImpl.sol";
import { DepositImpl } from "./DepositImpl.sol";
import { LiquidateOrVaporizeImpl } from "./LiquidateOrVaporizeImpl.sol";
import { TradeImpl } from "./TradeImpl.sol";
import { TransferImpl } from "./TransferImpl.sol";
import { WithdrawalImpl } from "./WithdrawalImpl.sol";


/**
 * @title OperationImpl
 * @author dYdX
 *
 * Logic for processing actions
 */
library OperationImpl {
    using Cache for Cache.MarketCache;
    using EnumerableSet for EnumerableSet.Set;
    using SafeMath for uint256;
    using Storage for Storage.State;
    using Types for Types.Par;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "OperationImpl";

    // ============ Public Functions ============

    function operate(
        Storage.State storage state,
        Account.Info[] memory accounts,
        Actions.ActionArgs[] memory actions
    )
        public
    {
        Events.logOperation();

        _verifyInputs(accounts, actions);

        (
            bool[] memory primaryAccounts,
            uint256[] memory numberOfMarketsWithBalancesPerAccount,
            Cache.MarketCache memory cache
        ) = _runPreprocessing(
            state,
            accounts,
            actions
        );

        _runActions(
            state,
            accounts,
            actions,
            cache
        );

        _verifyFinalState(
            state,
            accounts,
            primaryAccounts,
            numberOfMarketsWithBalancesPerAccount,
            cache
        );
    }

    // ============ Helper Functions ============

    function _verifyInputs(
        Account.Info[] memory accounts,
        Actions.ActionArgs[] memory actions
    )
        private
        pure
    {
        Require.that(
            accounts.length != 0,
            FILE,
            "Cannot have zero accounts"
        );
        Require.that(
            actions.length != 0,
            FILE,
            "Cannot have zero actions"
        );

        uint256 accountsLength = accounts.length;
        for (uint256 a; a < accountsLength; ++a) {
            for (uint256 b = a + 1; b < accountsLength; ++b) {
                Require.that(
                    !Account.equals(accounts[a], accounts[b]),
                    FILE,
                    "Cannot duplicate accounts",
                    accounts[a].owner,
                    accounts[a].number
                );
            }
        }
    }

    function _runPreprocessing(
        Storage.State storage state,
        Account.Info[] memory accounts,
        Actions.ActionArgs[] memory actions
    )
        private
        returns (
            bool[] memory,
            uint256[] memory,
            Cache.MarketCache memory
        )
    {
        bool[] memory primaryAccounts = new bool[](accounts.length);
        uint256[] memory numberOfMarketsWithBalancesPerAccount = new uint256[](accounts.length);
        Cache.MarketCache memory cache = Cache.create(state.numMarkets);

        // keep track of primary accounts and indexes that need updating
        uint256 actionsLength = actions.length;
        for (uint256 i; i < actionsLength; ++i) {
            Actions.ActionArgs memory arg = actions[i];
            Actions.ActionType actionType = arg.actionType;
            Actions.MarketLayout marketLayout = Actions.getMarketLayout(actionType);
            Actions.AccountLayout accountLayout = Actions.getAccountLayout(actionType);

            // parse out primary accounts
            if (accountLayout != Actions.AccountLayout.OnePrimary) {
                Require.that(
                    arg.accountId != arg.otherAccountId,
                    FILE,
                    "Duplicate accounts in action",
                    i
                );
                if (accountLayout == Actions.AccountLayout.TwoPrimary) {
                    primaryAccounts[arg.otherAccountId] = true;
                } else {
                    // accountLayout == Actions.AccountLayout.PrimaryAndSecondary
                    Require.that(
                        !primaryAccounts[arg.otherAccountId],
                        FILE,
                        "Requires non-primary account",
                        arg.otherAccountId
                    );
                }
            }
            primaryAccounts[arg.accountId] = true;

            // keep track of indexes to update
            if (marketLayout == Actions.MarketLayout.OneMarket) {
                _updateMarket(state, cache, arg.primaryMarketId);
            } else if (marketLayout == Actions.MarketLayout.TwoMarkets) {
                Require.that(
                    arg.primaryMarketId != arg.secondaryMarketId,
                    FILE,
                    "Duplicate markets in action",
                    i
                );
                _updateMarket(state, cache, arg.primaryMarketId);
                _updateMarket(state, cache, arg.secondaryMarketId);
            }
        }

        // get any other markets for which an account has a balance
        uint256 accountsLength = accounts.length;
        for (uint256 a = 0; a < accountsLength; a++) {
            uint[] memory marketIdsWithBalance = state.getMarketsWithBalances(accounts[a]);
            uint256 numMarketsWithBalance = marketIdsWithBalance.length;
            numberOfMarketsWithBalancesPerAccount[a] = numMarketsWithBalance;
            for (uint256 i; i < numMarketsWithBalance; ++i) {
                _updateMarket(state, cache, marketIdsWithBalance[i]);
            }
        }

        state.initializeCache(cache, /* fetchFreshIndex = */ false);

        uint256 numMarkets = cache.getNumMarkets();
        for (uint256 i; i < numMarkets; ++i) {
            Events.logOraclePrice(cache.getAtIndex(i));
        }

        return (primaryAccounts, numberOfMarketsWithBalancesPerAccount, cache);
    }

    function _updateMarket(
        Storage.State storage state,
        Cache.MarketCache memory cache,
        uint256 marketId
    )
        private
    {
        if (!cache.hasMarket(marketId)) {
            cache.set(marketId);
            Events.logIndexUpdate(marketId, state.updateIndex(marketId));
        }
    }

    function _runActions(
        Storage.State storage state,
        Account.Info[] memory accounts,
        Actions.ActionArgs[] memory actions,
        Cache.MarketCache memory cache
    )
        private
    {
        uint256 actionsLength = actions.length;
        for (uint256 i; i < actionsLength; ++i) {
            Actions.ActionArgs memory action = actions[i];
            Actions.ActionType actionType = action.actionType;

            if (actionType == Actions.ActionType.Deposit) {
                Actions.DepositArgs memory depositArgs = Actions.parseDepositArgs(accounts, action);
                DepositImpl.deposit(
                    state,
                    depositArgs,
                    cache.get(depositArgs.market).index
                );
            } else if (actionType == Actions.ActionType.Withdraw) {
                Actions.WithdrawArgs memory withdrawArgs = Actions.parseWithdrawArgs(accounts, action);
                WithdrawalImpl.withdraw(
                    state,
                    withdrawArgs,
                    cache.get(withdrawArgs.market).index
                );
            } else if (actionType == Actions.ActionType.Transfer) {
                Actions.TransferArgs memory transferArgs = Actions.parseTransferArgs(accounts, action);
                TransferImpl.transfer(
                    state,
                    transferArgs,
                    cache.get(transferArgs.market).index
                );
            } else if (actionType == Actions.ActionType.Buy) {
                Actions.BuyArgs memory buyArgs = Actions.parseBuyArgs(accounts, action);
                TradeImpl.buy(
                    state,
                    buyArgs,
                    cache.get(buyArgs.takerMarket).index,
                    cache.get(buyArgs.makerMarket).index
                );
            } else if (actionType == Actions.ActionType.Sell) {
                Actions.SellArgs memory sellArgs = Actions.parseSellArgs(accounts, action);
                TradeImpl.sell(
                    state,
                    sellArgs,
                    cache.get(sellArgs.takerMarket).index,
                    cache.get(sellArgs.makerMarket).index
                );
            } else if (actionType == Actions.ActionType.Trade) {
                Actions.TradeArgs memory tradeArgs = Actions.parseTradeArgs(accounts, action);
                TradeImpl.trade(
                    state,
                    tradeArgs,
                    cache.get(tradeArgs.inputMarket).index,
                    cache.get(tradeArgs.outputMarket).index
                );
            } else if (actionType == Actions.ActionType.Liquidate) {
                LiquidateOrVaporizeImpl.liquidate(
                    state,
                    cache,
                    Actions.parseLiquidateArgs(accounts, action)
                );
            } else if (actionType == Actions.ActionType.Vaporize) {
                LiquidateOrVaporizeImpl.vaporize(
                    state,
                    cache,
                    Actions.parseVaporizeArgs(accounts, action)
                );
            } else {
                assert(actionType == Actions.ActionType.Call);
                // solium-disable-next-line
                CallImpl.call(
                    state,
                    Actions.parseCallArgs(accounts, action)
                );
            }
        }
    }

    function _verifyFinalState(
        Storage.State storage state,
        Account.Info[] memory accounts,
        bool[] memory primaryAccounts,
        uint256[] memory numberOfMarketsWithBalancesPerAccount,
        Cache.MarketCache memory cache
    )
        private
    {
        bool isBorrowAllowed = state.riskParams.oracleSentinel.isBorrowAllowed();

        uint256 numMarkets = cache.getNumMarkets();
        for (uint256 i; i < numMarkets; ++i) {
            uint256 marketId = cache.getAtIndex(i).marketId;
            Types.TotalPar memory totalPar = state.getTotalPar(marketId);
            (
                Types.Wei memory totalSupplyWei,
                Types.Wei memory totalBorrowWei
            ) = Interest.totalParToWei(totalPar, cache.getAtIndex(i).index);

            Types.Wei memory maxBorrowWei = state.getMaxBorrowWei(marketId);
            Types.Wei memory maxSupplyWei = state.getMaxSupplyWei(marketId);

            // first check if the market is closing, borrowing is enabled, or if too much is being borrowed
            if (cache.getAtIndex(i).isClosing) {
                Require.that(
                    totalPar.borrow <= cache.getAtIndex(i).borrowPar,
                    FILE,
                    "Market is closing",
                    marketId
                );
            } else if (!isBorrowAllowed) {
                Require.that(
                    totalPar.borrow <= cache.getAtIndex(i).borrowPar,
                    FILE,
                    "Borrowing is currently disabled",
                    marketId
                );
            } else if (maxBorrowWei.value != 0) {
                // require total borrow is less than the max OR it scaled down
                Types.Wei memory cachedBorrowWei = Interest.parToWei(
                    Types.Par(true, cache.getAtIndex(i).borrowPar),
                    cache.getAtIndex(i).index
                );
                Require.that(
                    totalBorrowWei.value <= maxBorrowWei.value || totalBorrowWei.value <= cachedBorrowWei.value,
                    FILE,
                    "Total borrow exceeds max borrow",
                    marketId
                );
            }

            // check if too much is being supplied
            if (maxSupplyWei.value != 0) {
                // require total supply is less than the max OR it scaled down
                Types.Wei memory cachedSupplyWei = Interest.parToWei(
                    Types.Par(true, cache.getAtIndex(i).supplyPar),
                    cache.getAtIndex(i).index
                );
                Require.that(
                    totalSupplyWei.value <= maxSupplyWei.value || totalSupplyWei.value <= cachedSupplyWei.value,
                    FILE,
                    "Total supply exceeds max supply",
                    marketId
                );
            }

            // Log the current interest rate
            Interest.Rate memory rate = state.markets[marketId].interestSetter.getInterestRate(
                cache.getAtIndex(i).token,
                totalBorrowWei.value,
                totalSupplyWei.value
            );
            Events.logInterestRate(marketId, rate);
        }

        // verify account collateralization
        uint256 accountsLength = accounts.length;
        for (uint256 a = 0; a < accountsLength; a++) {
            Account.Info memory account = accounts[a];

            _verifyNumberOfMarketsWithBalances(state, account, numberOfMarketsWithBalancesPerAccount[a]);

            // don't check collateralization for non-primary accounts
            if (!primaryAccounts[a]) {
                continue;
            }

            // validate minBorrowedValue
            bool collateralized = state.isCollateralized(account, cache, /* requireMinBorrow = */ true);

            // check collateralization for primary accounts
            Require.that(
                collateralized,
                FILE,
                "Undercollateralized account",
                account.owner,
                account.number
            );

            // ensure status is normal for primary accounts
            if (state.getStatus(account) != Account.Status.Normal) {
                state.setStatus(account, Account.Status.Normal);
            }
        }
    }

    function _verifyNumberOfMarketsWithBalances(
        Storage.State storage state,
        Account.Info memory account,
        uint256 cachedNumberOfMarketsWithBalances
    )
        private view
    {
        // The account should either have less markets with balances than at the start of the transaction OR
        // less markets with balances than the max number of markets with balances per account
        uint256 actualNumberOfMarketsWithBalances = state.getNumberOfMarketsWithBalances(account);
        Require.that(
            actualNumberOfMarketsWithBalances <= cachedNumberOfMarketsWithBalances ||
            actualNumberOfMarketsWithBalances <= state.riskParams.accountMaxNumberOfMarketsWithBalances,
            FILE,
            "Too many non-zero balances",
            account.owner,
            account.number
        );
    }
}

File 40 of 50 : LiquidateOrVaporizeImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IExternalCallback } from "../interfaces/IExternalCallback.sol";
import { Account } from "../lib/Account.sol";
import { Actions } from "../lib/Actions.sol";
import { Cache } from "../lib/Cache.sol";
import { Decimal } from "../lib/Decimal.sol";
import { Events } from "../lib/Events.sol";
import { Interest } from "../lib/Interest.sol";
import { DolomiteMarginMath } from "../lib/DolomiteMarginMath.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Require } from "../lib/Require.sol";
import { SafeExternalCallback } from "../lib/SafeExternalCallback.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";


/**
 * @dev A library that combines shares code for executing Liquidation or Vaporization actions.
 */
library LiquidateOrVaporizeImpl {
    using Cache for Cache.MarketCache;
    using SafeMath for uint256;
    using Storage for Storage.State;
    using Types for Types.Par;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "LiquidateOrVaporizeImpl";

    // ============ Account Functions ============

    function liquidate(
        Storage.State storage state,
        Cache.MarketCache memory cache,
        Actions.LiquidateArgs memory args
    )
    public
    {
        state.requireIsGlobalOperator(msg.sender);
        Require.that(
            state.riskParams.oracleSentinel.isLiquidationAllowed(),
            FILE,
            "Liquidations are disabled"
        );

        // verify liquidatable
        if (Account.Status.Liquid != state.getStatus(args.liquidAccount)) {
            Require.that(
                !state.isCollateralized(args.liquidAccount, cache, /* requireMinBorrow = */ false),
                FILE,
                "Unliquidatable account",
                args.liquidAccount.owner,
                args.liquidAccount.number
            );
            state.setStatus(args.liquidAccount, Account.Status.Liquid);
        }

        Types.Wei memory maxHeldWei = state.getWei(
            args.liquidAccount,
            args.heldMarket,
            cache.get(args.heldMarket).index
        );

        Require.that(
            !maxHeldWei.isNegative(),
            FILE,
            "Collateral cannot be negative",
            args.heldMarket
        );

        Interest.Index memory owedIndex = cache.get(args.owedMarket).index;
        Interest.Index memory heldIndex = cache.get(args.heldMarket).index;
        (
            Types.Par memory owedPar,
            Types.Wei memory owedWei
        ) = state.getNewParAndDeltaWeiForLiquidation(
            args.liquidAccount,
            args.owedMarket,
            owedIndex,
            args.amount
        );

        (
            Monetary.Price memory heldPrice,
            Monetary.Price memory owedPriceAdj
        ) = _getLiquidationPrices(
            state,
            cache,
            args.liquidAccount,
            args.heldMarket,
            args.owedMarket
        );

        Types.Wei memory heldWei = _owedWeiToHeldWei(owedWei, heldPrice, owedPriceAdj);

        // if attempting to over-borrow the held asset, bound it by the maximum
        if (heldWei.value > maxHeldWei.value) {
            heldWei = maxHeldWei.negative();
            owedWei = _heldWeiToOwedWei(heldWei, heldPrice, owedPriceAdj);

            state.setPar(
                args.liquidAccount,
                args.heldMarket,
                Types.zeroPar()
            );
            state.setParFromDeltaWei(
                args.liquidAccount,
                args.owedMarket,
                owedIndex,
                owedWei
            );
        } else {
            state.setPar(
                args.liquidAccount,
                args.owedMarket,
                owedPar
            );
            state.setParFromDeltaWei(
                args.liquidAccount,
                args.heldMarket,
                heldIndex,
                heldWei
            );
        }

        // set the balances for the solid account
        state.setParFromDeltaWei(
            args.solidAccount,
            args.owedMarket,
            owedIndex,
            owedWei.negative()
        );
        state.setParFromDeltaWei(
            args.solidAccount,
            args.heldMarket,
            heldIndex,
            heldWei.negative()
        );

        uint256 callbackGasLimit = state.riskParams.callbackGasLimit;
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.liquidAccount,
            args.solidAccount,
            args.heldMarket,
            heldWei,
            args.owedMarket,
            owedWei,
            callbackGasLimit
        );
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.solidAccount,
            args.liquidAccount,
            args.heldMarket,
            heldWei.negative(),
            args.owedMarket,
            owedWei.negative(),
            callbackGasLimit
        );

        Events.logLiquidate(
            state,
            args,
            heldWei,
            owedWei
        );
    }

    function vaporize(
        Storage.State storage state,
        Cache.MarketCache memory cache,
        Actions.VaporizeArgs memory args
    )
    public
    {
        state.requireIsOperator(args.solidAccount, msg.sender);

        // verify vaporizable
        if (Account.Status.Vapor != state.getStatus(args.vaporAccount)) {
            Require.that(
                state.isVaporizable(args.vaporAccount, cache),
                FILE,
                "Unvaporizable account",
                args.vaporAccount.owner,
                args.vaporAccount.number
            );
            state.setStatus(args.vaporAccount, Account.Status.Vapor);
        }

        // First, attempt to refund using the same token
        // cache.get(args.owedMarket).index is not stored in a variable here to avoid the "stack too deep" error
        (
            bool fullyRepaid,
            Types.Wei memory excessWei
        ) = _vaporizeUsingExcess(state, args, cache.get(args.owedMarket).index);
        if (fullyRepaid) {
            Events.logVaporize(
                state,
                args,
                Types.zeroWei(),
                Types.zeroWei(),
                excessWei
            );
            return;
        }

        Types.Wei memory maxHeldWei = state.getNumExcessTokens(args.heldMarket);

        Require.that(
            !maxHeldWei.isNegative(),
            FILE,
            "Excess cannot be negative",
            args.heldMarket
        );

        (
            Types.Par memory owedPar,
            Types.Wei memory owedWei
        ) = state.getNewParAndDeltaWeiForLiquidation(
            args.vaporAccount,
            args.owedMarket,
            cache.get(args.owedMarket).index,
            args.amount
        );

        // For vaporizations, we do not want to use account-based risk overrides for the prices
        (
            Monetary.Price memory heldPrice,
            Monetary.Price memory owedPriceAdj
        ) = _getLiquidationPrices(
            state,
            cache,
            /* liquidAccount = */ Account.Info(address(0), 0),
            args.heldMarket,
            args.owedMarket
        );

        Types.Wei memory heldWei = _owedWeiToHeldWei(owedWei, heldPrice, owedPriceAdj);

        // if attempting to over-borrow the held asset, bound it by the maximum
        if (heldWei.value > maxHeldWei.value) {
            heldWei = maxHeldWei.negative();
            owedWei = _heldWeiToOwedWei(heldWei, heldPrice, owedPriceAdj);

            state.setParFromDeltaWei(
                args.vaporAccount,
                args.owedMarket,
                cache.get(args.owedMarket).index,
                owedWei
            );
        } else {
            state.setPar(
                args.vaporAccount,
                args.owedMarket,
                owedPar
            );
        }

        // set the balances for the solid account
        state.setParFromDeltaWei(
            args.solidAccount,
            args.owedMarket,
            cache.get(args.owedMarket).index,
            owedWei.negative()
        );
        state.setParFromDeltaWei(
            args.solidAccount,
            args.heldMarket,
            cache.get(args.heldMarket).index,
            heldWei.negative()
        );

        uint256 callbackGasLimit = state.riskParams.callbackGasLimit;
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.vaporAccount,
            args.solidAccount,
            args.heldMarket,
            Types.zeroWei(),
            args.owedMarket,
            owedWei,
            callbackGasLimit
        );
        SafeExternalCallback.callInternalBalanceChangeIfNecessary(
            args.solidAccount,
            args.vaporAccount,
            args.heldMarket,
            heldWei.negative(),
            args.owedMarket,
            owedWei.negative(),
            callbackGasLimit
        );

        Events.logVaporize(
            state,
            args,
            heldWei,
            owedWei,
            excessWei
        );
    }

    /**
     * Attempt to vaporize an account's balance using the excess tokens in the protocol. Return a
     * bool and a wei value. The boolean is true if and only if the balance was fully vaporized. The
     * Wei value is how many excess tokens were used to partially or fully vaporize the account's
     * negative balance.
     */
    function _vaporizeUsingExcess(
        Storage.State storage state,
        Actions.VaporizeArgs memory args,
        Interest.Index memory owedIndex
    )
    internal
    returns (bool, Types.Wei memory)
    {
        Types.Wei memory excessWei = state.getNumExcessTokens(args.owedMarket);

        // There are no excess funds, return zero
        if (!excessWei.isPositive()) {
            return (false, Types.zeroWei());
        }

        Types.Wei memory maxRefundWei = state.getWei(
            args.vaporAccount,
            args.owedMarket,
            owedIndex
        );
        maxRefundWei.sign = true;

        // The account is fully vaporizable using excess funds
        if (excessWei.value >= maxRefundWei.value) {
            state.setPar(
                args.vaporAccount,
                args.owedMarket,
                Types.zeroPar()
            );
            return (true, maxRefundWei);
        }

        // The account is only partially vaporizable using excess funds
        else {
            state.setParFromDeltaWei(
                args.vaporAccount,
                args.owedMarket,
                owedIndex,
                excessWei
            );
            return (false, excessWei);
        }
    }

    /**
     * For the purposes of liquidation or vaporization, get the value-equivalent amount of owedWei
     * given heldWei and the (spread-adjusted) prices of each asset.
     */
    function _heldWeiToOwedWei(
        Types.Wei memory heldWei,
        Monetary.Price memory heldPrice,
        Monetary.Price memory owedPrice
    )
    internal
    pure
    returns (Types.Wei memory)
    {
        return Types.Wei({
            sign: true,
            value: DolomiteMarginMath.getPartialRoundUp(heldWei.value, heldPrice.value, owedPrice.value)
        });
    }

    /**
     * For the purposes of liquidation or vaporization, get the value-equivalent amount of heldWei
     * given owedWei and the (spread-adjusted) prices of each asset.
     */
    function _owedWeiToHeldWei(
        Types.Wei memory owedWei,
        Monetary.Price memory heldPrice,
        Monetary.Price memory owedPrice
    )
    internal
    pure
    returns (Types.Wei memory)
    {
        return Types.Wei({
            sign: false,
            value: DolomiteMarginMath.getPartial(owedWei.value, owedPrice.value, heldPrice.value)
        });
    }

    /**
     * Return the (spread-adjusted) prices of two assets for the purposes of liquidation or
     * vaporization.
     */
    function _getLiquidationPrices(
        Storage.State storage state,
        Cache.MarketCache memory cache,
        Account.Info memory liquidAccount,
        uint256 heldMarketId,
        uint256 owedMarketId
    )
    internal
    view
    returns (
        Monetary.Price memory,
        Monetary.Price memory
    )
    {
        uint256 owedPrice = cache.get(owedMarketId).price.value;
        Decimal.D256 memory spread = state.getLiquidationSpreadForAccountAndPair(
            liquidAccount,
            heldMarketId,
            owedMarketId
        );

        Monetary.Price memory owedPriceAdj = Monetary.Price({
            value: owedPrice.add(Decimal.mul(owedPrice, spread))
        });

        return (cache.get(heldMarketId).price, owedPriceAdj);
    }
}

File 41 of 50 : GettersImpl.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { IAccountRiskOverrideSetter } from "../interfaces/IAccountRiskOverrideSetter.sol";
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "../interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";

import { Account } from "../lib/Account.sol";
import { Cache } from "../lib/Cache.sol";
import { Decimal } from "../lib/Decimal.sol";
import { DolomiteMarginMath } from "../lib/DolomiteMarginMath.sol";
import { Interest } from "../lib/Interest.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Require } from "../lib/Require.sol";
import { Storage } from "../lib/Storage.sol";
import { Token } from "../lib/Token.sol";
import { Types } from "../lib/Types.sol";


/**
 * @title GettersImpl
 * @author Dolomite
 *
 * Getter functions for data retrieval
 */
library GettersImpl {
    using Cache for Cache.MarketCache;
    using DolomiteMarginMath for uint256;
    using Storage for Storage.State;
    using Token for address;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 internal constant FILE = "GettersImpl";

    uint256 internal constant SECONDS_PER_YEAR = 365 days;

    // ============ Public Functions ============

    function getMarginRatio(
        Storage.State storage state
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return state.riskParams.marginRatio;
    }

    function getMarginRatioForAccount(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        (Decimal.D256 memory marginRatio,) = state.getAccountRiskOverride(account);
        if (marginRatio.value == 0) {
            marginRatio = state.riskParams.marginRatio;
        }
        return marginRatio;
    }

    function getLiquidationSpread(
        Storage.State storage state
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return state.riskParams.liquidationSpread;
    }

    function getEarningsRate(
        Storage.State storage state
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return state.riskParams.earningsRate;
    }

    function getMinBorrowedValue(
        Storage.State storage state
    )
        public
        view
        returns (Monetary.Value memory)
    {
        return state.riskParams.minBorrowedValue;
    }

    function getAccountMaxNumberOfMarketsWithBalances(
        Storage.State storage state
    )
        public
        view
        returns (uint256)
    {
        return state.riskParams.accountMaxNumberOfMarketsWithBalances;
    }

    function getOracleSentinel(
        Storage.State storage state
    )
        public
        view
        returns (IOracleSentinel)
    {
        return state.riskParams.oracleSentinel;
    }

    function getIsBorrowAllowed(
        Storage.State storage state
    )
        public
        view
        returns (bool)
    {
        return state.riskParams.oracleSentinel.isBorrowAllowed();
    }

    function getIsLiquidationAllowed(
        Storage.State storage state
    )
        public
        view
        returns (bool)
    {
        return state.riskParams.oracleSentinel.isLiquidationAllowed();
    }

    function getCallbackGasLimit(
        Storage.State storage state
    )
        public
        view
        returns (uint256)
    {
        return state.riskParams.callbackGasLimit;
    }

    function getDefaultAccountRiskOverrideSetter(
        Storage.State storage state
    )
        public
        view
        returns (IAccountRiskOverrideSetter)
    {
        return state.riskParams.defaultAccountRiskOverrideSetter;
    }

    function getAccountRiskOverrideSetterByAccountOwner(
        Storage.State storage state,
        address accountOwner
    )
        public
        view
        returns (IAccountRiskOverrideSetter)
    {
        return state.riskParams.accountRiskOverrideSetterMap[accountOwner];
    }

    function getAccountRiskOverrideByAccount(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory marginRatioOverride, Decimal.D256 memory liquidationSpreadOverride)
    {
        (marginRatioOverride, liquidationSpreadOverride) = state.getAccountRiskOverride(account);
    }

    function getMarginRatioOverrideByAccount(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory marginRatioOverride)
    {
        (marginRatioOverride,) = state.getAccountRiskOverride(account);
    }

    function getLiquidationSpreadOverrideByAccount(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory liquidationSpreadOverride)
    {
        (, liquidationSpreadOverride) = state.getAccountRiskOverride(account);
    }

    function getRiskLimits(
        Storage.State storage state
    )
        public
        view
        returns (Storage.RiskLimits memory)
    {
        return state.riskLimits;
    }

    function getNumMarkets(
        Storage.State storage state
    )
        public
        view
        returns (uint256)
    {
        return state.numMarkets;
    }

    function getMarketTokenAddress(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (address)
    {
        _requireValidMarket(state, marketId);
        return state.getToken(marketId);
    }

    function getMarketIdByTokenAddress(
        Storage.State storage state,
        address token
    )
        public
        view
        returns (uint256)
    {
        _requireValidToken(state, token);
        return state.tokenToMarketId[token];
    }

    function getMarketIsClosing(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (bool)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].isClosing;
    }

    function getMarketPrice(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Monetary.Price memory)
    {
        _requireValidMarket(state, marketId);
        return state.fetchPrice(marketId, state.getToken(marketId));
    }

    function getMarketTotalPar(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Types.TotalPar memory)
    {
        _requireValidMarket(state, marketId);
        return state.getTotalPar(marketId);
    }

    function getMarketTotalWei(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Types.TotalWei memory)
    {
        _requireValidMarket(state, marketId);
        Types.TotalPar memory totalPar = getMarketTotalPar(state, marketId);
        Interest.Index memory index = getMarketCurrentIndex(state, marketId);
        (Types.Wei memory supplyWei, Types.Wei memory borrowWei) = Interest.totalParToWei(totalPar, index);

        return Types.TotalWei({
            borrow: borrowWei.value.to128(),
            supply: supplyWei.value.to128()
        });
    }

    function getMarketCachedIndex(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Interest.Index memory)
    {
        _requireValidMarket(state, marketId);
        return state.getIndex(marketId);
    }

    function getMarketCurrentIndex(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Interest.Index memory)
    {
        _requireValidMarket(state, marketId);
        return state.fetchNewIndex(marketId, state.getIndex(marketId));
    }

    function getMarketPriceOracle(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (IPriceOracle)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].priceOracle;
    }

    function getMarketInterestSetter(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (IInterestSetter)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].interestSetter;
    }

    function getMarketMarginPremium(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].marginPremium;
    }

    function getMarketLiquidationSpreadPremium(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].liquidationSpreadPremium;
    }

    function getMarketMaxSupplyWei(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].maxSupplyWei;
    }

    function getMarketMaxBorrowWei(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].maxBorrowWei;
    }

    function getMarketEarningsRateOverride(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId].earningsRateOverride;
    }

    function getMarketBorrowInterestRatePerSecond(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        _requireValidMarket(state, marketId);
        return state.fetchInterestRate(
            marketId,
            state.getIndex(marketId)
        );
    }

    function getMarketBorrowInterestRateApr(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        Interest.Rate memory rate = getMarketBorrowInterestRatePerSecond(state, marketId);
        rate.value = rate.value * SECONDS_PER_YEAR;
        return rate;
    }

    function getMarketSupplyInterestRateApr(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        Types.TotalWei memory totalWei = getMarketTotalWei(state, marketId);
        if (totalWei.supply == 0) {
            return Interest.Rate({
                value: 0
            });
        }

        Interest.Rate memory borrowRate = getMarketBorrowInterestRateApr(state, marketId);
        Decimal.D256 memory earningsRate = getMarketEarningsRateOverride(state, marketId);
        if (earningsRate.value == 0) {
            earningsRate = getEarningsRate(state);
        }

        uint256 supplyRate = Decimal.mul(borrowRate.value, earningsRate);
        if (totalWei.borrow < totalWei.supply) {
            // scale down the interest by the amount being supplied. Why? Because interest is only being paid on
            // the borrowWei, which means it's split amongst all of the supplyWei. Scaling it down normalizes it
            // for the suppliers to share what's being paid by borrowers
            supplyRate = supplyRate.getPartial(totalWei.borrow, totalWei.supply);
        }

        return Interest.Rate({
            value: supplyRate
        });
    }

    function getLiquidationSpreadForAccountAndPair(
        Storage.State storage state,
        Account.Info memory account,
        uint256 heldMarketId,
        uint256 owedMarketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        _requireValidMarket(state, heldMarketId);
        _requireValidMarket(state, owedMarketId);
        return state.getLiquidationSpreadForAccountAndPair(account, heldMarketId, owedMarketId);
    }

    function getMarket(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Storage.Market memory)
    {
        _requireValidMarket(state, marketId);
        return state.markets[marketId];
    }

    function getMarketWithInfo(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (
            Storage.Market memory,
            Interest.Index memory,
            Monetary.Price memory,
            Interest.Rate memory
        )
    {
        _requireValidMarket(state, marketId);
        return (
            getMarket(state, marketId),
            getMarketCurrentIndex(state, marketId),
            getMarketPrice(state, marketId),
            getMarketBorrowInterestRatePerSecond(state, marketId)
        );
    }

    function getNumExcessTokens(
        Storage.State storage state,
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        _requireValidMarket(state, marketId);
        return state.getNumExcessTokens(marketId);
    }

    function getAccountPar(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId
    )
        public
        view
        returns (Types.Par memory)
    {
        _requireValidMarket(state, marketId);
        return state.getPar(account, marketId);
    }

    function getAccountWei(
        Storage.State storage state,
        Account.Info memory account,
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        _requireValidMarket(state, marketId);
        return Interest.parToWei(
            state.getPar(account, marketId),
            state.fetchNewIndex(marketId, state.getIndex(marketId))
        );
    }

    function getAccountStatus(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Account.Status)
    {
        return state.getStatus(account);
    }

    function getAccountMarketsWithBalances(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (uint256[] memory)
    {
        return state.getMarketsWithBalances(account);
    }

    function getAccountNumberOfMarketsWithBalances(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (uint256)
    {
        return state.getNumberOfMarketsWithBalances(account);
    }

    function getAccountMarketWithBalanceAtIndex(
        Storage.State storage state,
        Account.Info memory account,
        uint256 index
    )
        public
        view
        returns (uint256)
    {
        return state.getAccountMarketWithBalanceAtIndex(account, index);
    }

    function getAccountNumberOfMarketsWithDebt(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (uint256)
    {
        return state.getAccountNumberOfMarketsWithDebt(account);
    }

    function getAccountValues(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Monetary.Value memory, Monetary.Value memory)
    {
        return _getAccountValuesInternal(state, account, /* adjustForLiquidity = */ false);
    }

    function getAdjustedAccountValues(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (Monetary.Value memory, Monetary.Value memory)
    {
        return _getAccountValuesInternal(state, account, /* adjustForLiquidity = */ true);
    }

    function getAccountBalances(
        Storage.State storage state,
        Account.Info memory account
    )
        public
        view
        returns (
            uint[] memory,
            address[] memory,
            Types.Par[] memory,
            Types.Wei[] memory
        )
    {
        uint256[] memory markets = state.getMarketsWithBalances(account);
        address[] memory tokens = new address[](markets.length);
        Types.Par[] memory pars = new Types.Par[](markets.length);
        Types.Wei[] memory weis = new Types.Wei[](markets.length);

        uint256 length = markets.length;
        for (uint256 i; i < length; ++i) {
            tokens[i] = getMarketTokenAddress(state, markets[i]);
            pars[i] = getAccountPar(state, account, markets[i]);
            weis[i] = getAccountWei(state, account, markets[i]);
        }

        return (
            markets,
            tokens,
            pars,
            weis
        );
    }

    function getIsLocalOperator(
        Storage.State storage state,
        address owner,
        address operator
    )
        public
        view
        returns (bool)
    {
        return state.isLocalOperator(owner, operator);
    }

    function getIsGlobalOperator(
        Storage.State storage state,
        address operator
    )
        public
        view
        returns (bool)
    {
        return state.isGlobalOperator(operator);
    }

    function getIsAutoTraderSpecial(
        Storage.State storage state,
        address autoTrader
    )
        public
        view
        returns (bool)
    {
        return state.isAutoTraderSpecial(autoTrader);
    }

    // ============ Internal/Private Helper Functions ============

    /**
     * Revert if marketId is invalid.
     */
    function _requireValidMarket(
        Storage.State storage state,
        uint256 marketId
    )
    internal
    view
    {
        Require.that(
            marketId < state.numMarkets && state.markets[marketId].token != address(0),
            FILE,
            "Invalid market"
        );
    }

    function _requireValidToken(
        Storage.State storage state,
        address token
    )
    private
    view
    {
        Require.that(
            token == state.markets[state.tokenToMarketId[token]].token,
            FILE,
            "Invalid token"
        );
    }

    /**
     * Private helper for getting the monetary values of an account.
     */
    function _getAccountValuesInternal(
        Storage.State storage state,
        Account.Info memory account,
        bool adjustForLiquidity
    )
    private
    view
    returns (Monetary.Value memory, Monetary.Value memory)
    {
        uint256[] memory markets = state.getMarketsWithBalances(account);

        // populate cache
        Cache.MarketCache memory cache = Cache.create(markets.length);
        uint256 length = markets.length;
        for (uint256 i; i < length; ++i) {
            cache.set(markets[i]);
        }
        state.initializeCache(cache, /* fetchFreshIndex = */ true);

        (Decimal.D256 memory marginRatioOverride,) = state.getAccountRiskOverride(account);
        return state.getAccountValues(account, cache, adjustForLiquidity, marginRatioOverride);
    }
}

File 42 of 50 : DepositImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Actions } from "../lib/Actions.sol";
import { Events } from "../lib/Events.sol";
import { Exchange } from "../lib/Exchange.sol";
import { Interest } from "../lib/Interest.sol";
import { Require } from "../lib/Require.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";


library DepositImpl {
    using Storage for Storage.State;

    // ============ Constants ============

    bytes32 private constant FILE = "DepositImpl";

    // ============ Account Actions ============

    function deposit(
        Storage.State storage state,
        Actions.DepositArgs memory args,
        Interest.Index memory index
    )
        public
    {
        state.requireIsOperator(args.account, msg.sender);

        Require.that(
            args.from == msg.sender || args.from == args.account.owner,
            FILE,
            "Invalid deposit source",
            args.from
        );

        (
            Types.Par memory newPar,
            Types.Wei memory deltaWei
        ) = state.getNewParAndDeltaWei(
            args.account,
            args.market,
            index,
            args.amount
        );

        state.setPar(
            args.account,
            args.market,
            newPar
        );

        // requires a positive deltaWei
        Exchange.transferIn(
            state.getToken(args.market),
            args.from,
            deltaWei
        );

        Events.logDeposit(
            state,
            args,
            deltaWei
        );
    }
}

File 43 of 50 : CallImpl.sol
/*

    Copyright 2021 Dolomite

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { ICallee } from "../interfaces/ICallee.sol";
import { Actions } from "../lib/Actions.sol";
import { Cache } from "../lib/Cache.sol";
import { Events } from "../lib/Events.sol";
import { Storage } from "../lib/Storage.sol";


library CallImpl {
    using Cache for Cache.MarketCache;
    using Storage for Storage.State;

    // ============ Constants ============

    bytes32 private constant FILE = "CallImpl";

    // ============ Account Actions ============

    function call(
        Storage.State storage state,
        Actions.CallArgs memory args
    )
    public
    {
        state.requireIsOperator(args.account, msg.sender);

        ICallee(args.callee).callFunction(
            msg.sender,
            args.account,
            args.data
        );

        Events.logCall(args);
    }
}

File 44 of 50 : AdminImpl.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Address } from "@openzeppelin/contracts/utils/Address.sol";

import { IERC20Detailed } from "../interfaces/IERC20Detailed.sol";
import { IAccountRiskOverrideSetter } from "../interfaces/IAccountRiskOverrideSetter.sol";
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "../interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";

import { Account } from "../lib/Account.sol";
import { Decimal } from "../lib/Decimal.sol";
import { Interest } from "../lib/Interest.sol";
import { DolomiteMarginMath } from "../lib/DolomiteMarginMath.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Require } from "../lib/Require.sol";
import { Storage } from "../lib/Storage.sol";
import { Token } from "../lib/Token.sol";
import { Types } from "../lib/Types.sol";


/**
 * @title AdminImpl
 * @author dYdX
 *
 * Administrative functions to keep the protocol updated
 */
library AdminImpl {
    using Address for address;
    using DolomiteMarginMath for uint256;
    using Storage for Storage.State;
    using Token for address;
    using Types for Types.Wei;

    // ============ Constants ============

    bytes32 private constant FILE = "AdminImpl";

    // ============ Events ============

    event LogWithdrawExcessTokens(
        address token,
        uint256 amount
    );

    event LogWithdrawUnsupportedTokens(
        address token,
        uint256 amount
    );

    event LogAddMarket(
        uint256 marketId,
        address token
    );

    event LogSetIsClosing(
        uint256 marketId,
        bool isClosing
    );

    event LogSetPriceOracle(
        uint256 marketId,
        address priceOracle
    );

    event LogSetInterestSetter(
        uint256 marketId,
        address interestSetter
    );

    event LogSetMarginPremium(
        uint256 marketId,
        Decimal.D256 marginPremium
    );

    event LogSetLiquidationSpreadPremium(
        uint256 marketId,
        Decimal.D256 liquidationSpreadPremium
    );

    event LogSetMaxSupplyWei(
        uint256 marketId,
        Types.Wei maxSupplyWei
    );

    event LogSetMaxBorrowWei(
        uint256 marketId,
        Types.Wei maxBorrowWei
    );

    event LogSetEarningsRateOverride(
        uint256 marketId,
        Decimal.D256 earningsRateOverride
    );

    event LogSetMarginRatio(
        Decimal.D256 marginRatio
    );

    event LogSetLiquidationSpread(
        Decimal.D256 liquidationSpread
    );

    event LogSetEarningsRate(
        Decimal.D256 earningsRate
    );

    event LogSetMinBorrowedValue(
        Monetary.Value minBorrowedValue
    );

    event LogSetAccountMaxNumberOfMarketsWithBalances(
        uint256 accountMaxNumberOfMarketsWithBalances
    );

    event LogSetOracleSentinel(
        IOracleSentinel oracleSentinel
    );

    event LogSetCallbackGasLimit(
        uint256 callbackGasLimit
    );

    event LogSetDefaultAccountRiskOverrideSetter(
        IAccountRiskOverrideSetter defaultAccountRiskOverrideSetter
    );

    event LogSetAccountRiskOverrideSetter(
        address accountOwner,
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    );

    event LogSetGlobalOperator(
        address operator,
        bool approved
    );

    event LogSetAutoTraderIsSpecial(
        address autoTrader,
        bool isSpecial
    );

    // ============ Token Functions ============

    function ownerWithdrawExcessTokens(
        Storage.State storage state,
        uint256 marketId,
        address recipient
    )
    public
    returns (uint256)
    {
        _validateMarketId(state, marketId);
        Types.Wei memory excessWei = state.getNumExcessTokens(marketId);

        Require.that(
            !excessWei.isNegative(),
            FILE,
            "Negative excess"
        );

        address token = state.getToken(marketId);

        uint256 actualBalance = IERC20Detailed(token).balanceOf(address(this));
        if (excessWei.value > actualBalance) {
            excessWei.value = actualBalance;
        }

        token.transfer(recipient, excessWei.value);

        emit LogWithdrawExcessTokens(token, excessWei.value);

        return excessWei.value;
    }

    function ownerWithdrawUnsupportedTokens(
        Storage.State storage state,
        address token,
        address recipient
    )
    public
    returns (uint256)
    {
        _requireNoMarket(state, token);

        uint256 balance = IERC20Detailed(token).balanceOf(address(this));
        token.transfer(recipient, balance);

        emit LogWithdrawUnsupportedTokens(token, balance);

        return balance;
    }

    // ============ Market Functions ============

    function ownerAddMarket(
        Storage.State storage state,
        address token,
        IPriceOracle priceOracle,
        IInterestSetter interestSetter,
        Decimal.D256 memory marginPremium,
        Decimal.D256 memory liquidationSpreadPremium,
        uint256 maxSupplyWei,
        uint256 maxBorrowWei,
        Decimal.D256 memory earningsRateOverride,
        bool isClosing
    )
    public
    {
        _requireNoMarket(state, token);

        uint256 marketId = state.numMarkets;
        state.numMarkets += 1;

        state.markets[marketId].token = token;
        state.markets[marketId].index = Interest.newIndex();
        state.markets[marketId].isClosing = isClosing;
        state.tokenToMarketId[token] = marketId;

        emit LogAddMarket(marketId, token);
        if (isClosing) {
            emit LogSetIsClosing(marketId, isClosing);
        }

        _setPriceOracle(state, marketId, priceOracle);
        _setInterestSetter(state, marketId, interestSetter);
        _setMarginPremium(state, marketId, marginPremium);
        _setLiquidationSpreadPremium(state, marketId, liquidationSpreadPremium);
        _setMaxSupplyWei(state, marketId, maxSupplyWei);
        _setMaxBorrowWei(state, marketId, maxBorrowWei);
        _setEarningsRateOverride(state, marketId, earningsRateOverride);
    }

    function ownerSetIsClosing(
        Storage.State storage state,
        uint256 marketId,
        bool isClosing
    )
    public
    {
        _validateMarketId(state, marketId);

        state.markets[marketId].isClosing = isClosing;
        emit LogSetIsClosing(marketId, isClosing);
    }

    function ownerSetPriceOracle(
        Storage.State storage state,
        uint256 marketId,
        IPriceOracle priceOracle
    )
    public
    {
        _validateMarketId(state, marketId);
        _setPriceOracle(state, marketId, priceOracle);
    }

    function ownerSetInterestSetter(
        Storage.State storage state,
        uint256 marketId,
        IInterestSetter interestSetter
    )
    public
    {
        _validateMarketId(state, marketId);
        _setInterestSetter(state, marketId, interestSetter);
    }

    function ownerSetMarginPremium(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory marginPremium
    )
    public
    {
        _validateMarketId(state, marketId);
        _setMarginPremium(state, marketId, marginPremium);
    }

    function ownerSetLiquidationSpreadPremium(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory liquidationSpreadPremium
    )
    public
    {
        _validateMarketId(state, marketId);
        _setLiquidationSpreadPremium(state, marketId, liquidationSpreadPremium);
    }

    function ownerSetMaxSupplyWei(
        Storage.State storage state,
        uint256 marketId,
        uint256 maxSupplyWei
    )
    public
    {
        _validateMarketId(state, marketId);
        _setMaxSupplyWei(state, marketId, maxSupplyWei);
    }

    function ownerSetMaxBorrowWei(
        Storage.State storage state,
        uint256 marketId,
        uint256 maxBorrowWei
    )
    public
    {
        _validateMarketId(state, marketId);
        _setMaxBorrowWei(state, marketId, maxBorrowWei);
    }

    function ownerSetEarningsRateOverride(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory earningsRateOverride
    )
    public
    {
        _validateMarketId(state, marketId);
        _setEarningsRateOverride(state, marketId, earningsRateOverride);
    }

    // ============ Risk Functions ============

    function ownerSetMarginRatio(
        Storage.State storage state,
        Decimal.D256 memory ratio
    )
    public
    {
        Require.that(
            ratio.value <= state.riskLimits.marginRatioMax,
            FILE,
            "Ratio too high"
        );
        Require.that(
            ratio.value > state.riskParams.liquidationSpread.value,
            FILE,
            "Ratio cannot be <= spread"
        );
        state.riskParams.marginRatio = ratio;
        emit LogSetMarginRatio(ratio);
    }

    function ownerSetLiquidationSpread(
        Storage.State storage state,
        Decimal.D256 memory spread
    )
    public
    {
        Require.that(
            spread.value <= state.riskLimits.liquidationSpreadMax,
            FILE,
            "Spread too high"
        );
        Require.that(
            spread.value < state.riskParams.marginRatio.value,
            FILE,
            "Spread cannot be >= ratio"
        );
        state.riskParams.liquidationSpread = spread;
        emit LogSetLiquidationSpread(spread);
    }

    function ownerSetEarningsRate(
        Storage.State storage state,
        Decimal.D256 memory earningsRate
    )
    public
    {
        Require.that(
            earningsRate.value <= state.riskLimits.earningsRateMax,
            FILE,
            "Rate too high"
        );
        state.riskParams.earningsRate = earningsRate;
        emit LogSetEarningsRate(earningsRate);
    }

    function ownerSetMinBorrowedValue(
        Storage.State storage state,
        Monetary.Value memory minBorrowedValue
    )
    public
    {
        Require.that(
            minBorrowedValue.value <= state.riskLimits.minBorrowedValueMax,
            FILE,
            "Value too high"
        );
        state.riskParams.minBorrowedValue = minBorrowedValue;
        emit LogSetMinBorrowedValue(minBorrowedValue);
    }

    function ownerSetAccountMaxNumberOfMarketsWithBalances(
        Storage.State storage state,
        uint256 accountMaxNumberOfMarketsWithBalances
    ) public {
        Require.that(
            accountMaxNumberOfMarketsWithBalances >= 2,
            FILE,
            "Max number of markets too low"
        );
        Require.that(
            accountMaxNumberOfMarketsWithBalances <= 64,
            FILE,
            "Max number of markets too high"
        );
        state.riskParams.accountMaxNumberOfMarketsWithBalances = accountMaxNumberOfMarketsWithBalances;
        emit LogSetAccountMaxNumberOfMarketsWithBalances(accountMaxNumberOfMarketsWithBalances);
    }

    function ownerSetOracleSentinel(
        Storage.State storage state,
        IOracleSentinel oracleSentinel
    ) public {
        Require.that(
            oracleSentinel.isBorrowAllowed() && oracleSentinel.isLiquidationAllowed(),
            FILE,
            "Invalid oracle sentinel"
        );
        state.riskParams.oracleSentinel = oracleSentinel;
        emit LogSetOracleSentinel(oracleSentinel);
    }

    function ownerSetCallbackGasLimit(
        Storage.State storage state,
        uint256 callbackGasLimit
    ) public {
        // Allow setting to any value.
        // Setting to 0 will effectively disable callbacks; setting it super large is not desired since it could lead to
        // DOS attacks on the protocol; however, hard coding a max value isn't preferred since some chains can calculate
        // gas usage differently (like ArbGas before Arbitrum rolled out nitro)
        state.riskParams.callbackGasLimit = callbackGasLimit;
        emit LogSetCallbackGasLimit(callbackGasLimit);
    }

    function ownerSetDefaultAccountRiskOverride(
        Storage.State storage state,
        IAccountRiskOverrideSetter defaultAccountRiskOverrideSetter
    ) public {
        if (address(defaultAccountRiskOverrideSetter) != address(0)) {
            (
                Decimal.D256 memory marginRatio,
                Decimal.D256 memory liquidationSpread
            ) = defaultAccountRiskOverrideSetter.getAccountRiskOverride(_getDefaultAccount(msg.sender));
            state.validateAccountRiskOverrideValues(marginRatio, liquidationSpread);
        }

        state.riskParams.defaultAccountRiskOverrideSetter = defaultAccountRiskOverrideSetter;
        emit LogSetDefaultAccountRiskOverrideSetter(defaultAccountRiskOverrideSetter);
    }

    function ownerSetAccountRiskOverride(
        Storage.State storage state,
        address accountOwner,
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    ) public {
        if (address(accountRiskOverrideSetter) != address(0)) {
            (
                Decimal.D256 memory marginRatio,
                Decimal.D256 memory liquidationSpread
            ) = accountRiskOverrideSetter.getAccountRiskOverride(_getDefaultAccount(accountOwner));
            state.validateAccountRiskOverrideValues(marginRatio, liquidationSpread);
        }

        state.riskParams.accountRiskOverrideSetterMap[accountOwner] = accountRiskOverrideSetter;
        emit LogSetAccountRiskOverrideSetter(accountOwner, accountRiskOverrideSetter);
    }

    // ============ Global Operator Functions ============

    function ownerSetGlobalOperator(
        Storage.State storage state,
        address operator,
        bool approved
    )
    public
    {
        state.globalOperators[operator] = approved ? 1 : 2;

        emit LogSetGlobalOperator(operator, approved);
    }

    function ownerSetAutoTraderSpecial(
        Storage.State storage state,
        address autoTrader,
        bool isSpecial
    )
    public
    {
        state.specialAutoTraders[autoTrader] = isSpecial ? 1 : 2;

        emit LogSetAutoTraderIsSpecial(autoTrader, isSpecial);
    }

    // ============ Private Functions ============

    function _setPriceOracle(
        Storage.State storage state,
        uint256 marketId,
        IPriceOracle priceOracle
    )
    private
    {
        // require oracle can return non-zero price
        address token = state.markets[marketId].token;

        Require.that(
            priceOracle.getPrice(token).value != 0,
            FILE,
            "Invalid oracle price"
        );

        state.markets[marketId].priceOracle = priceOracle;

        emit LogSetPriceOracle(marketId, address(priceOracle));
    }

    function _setInterestSetter(
        Storage.State storage state,
        uint256 marketId,
        IInterestSetter interestSetter
    )
    private
    {
        // ensure interestSetter can return a value without reverting
        address token = state.markets[marketId].token;
        interestSetter.getInterestRate(token, 0, 0);

        state.markets[marketId].interestSetter = interestSetter;

        emit LogSetInterestSetter(marketId, address(interestSetter));
    }

    function _setMarginPremium(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory marginPremium
    )
    private
    {
        Require.that(
            marginPremium.value <= state.riskLimits.marginPremiumMax,
            FILE,
            "Margin premium too high"
        );
        state.markets[marketId].marginPremium = marginPremium;

        emit LogSetMarginPremium(marketId, marginPremium);
    }

    function _setLiquidationSpreadPremium(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory liquidationSpreadPremium
    )
    private
    {
        Require.that(
            liquidationSpreadPremium.value <= state.riskLimits.liquidationSpreadPremiumMax,
            FILE,
            "Spread premium too high"
        );
        state.markets[marketId].liquidationSpreadPremium = liquidationSpreadPremium;

        emit LogSetLiquidationSpreadPremium(marketId, liquidationSpreadPremium);
    }

    function _setMaxSupplyWei(
        Storage.State storage state,
        uint256 marketId,
        uint256 maxSupplyWei
    )
    private
    {
        Types.Wei memory maxSupplyWeiStruct = Types.Wei(true, maxSupplyWei.to128());
        state.markets[marketId].maxSupplyWei = maxSupplyWeiStruct;

        emit LogSetMaxSupplyWei(marketId, maxSupplyWeiStruct);
    }

    function _setMaxBorrowWei(
        Storage.State storage state,
        uint256 marketId,
        uint256 maxBorrowWei
    )
    private
    {
        Types.Wei memory maxBorrowWeiStruct = Types.Wei(false, maxBorrowWei.to128());
        state.markets[marketId].maxBorrowWei = maxBorrowWeiStruct;

        emit LogSetMaxBorrowWei(marketId, maxBorrowWeiStruct);
    }

    function _setEarningsRateOverride(
        Storage.State storage state,
        uint256 marketId,
        Decimal.D256 memory earningsRateOverride
    )
    private
    {
        Require.that(
            earningsRateOverride.value <= state.riskLimits.earningsRateMax,
            FILE,
            "Earnings rate override too high"
        );

        state.markets[marketId].earningsRateOverride = earningsRateOverride;

        emit LogSetEarningsRateOverride(marketId, earningsRateOverride);
    }

    function _requireNoMarket(
        Storage.State storage state,
        address token
    )
    private
    view
    {
        // not-found case is marketId of 0. 0 is a valid market ID so we need to check market ID 0's token equality.
        uint marketId = state.tokenToMarketId[token];
        bool marketExists = token == state.markets[marketId].token;

        Require.that(
            !marketExists,
            FILE,
            "Market exists"
        );
    }

    function _validateMarketId(
        Storage.State storage state,
        uint256 marketId
    )
    private
    view
    {
        Require.that(
            marketId < state.numMarkets && state.markets[marketId].token != address(0),
            FILE,
            "Invalid market",
            marketId
        );
    }

    function _getDefaultAccount(
        address _accountOwner
    )
    private
    pure
    returns (Account.Info memory)
    {
        return Account.Info({
            owner: _accountOwner,
            number: 0
        });
    }
}

File 45 of 50 : Permission.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Types } from "./lib/Types.sol";

import { HasState } from "./HasState.sol";


/**
 * @title Permission
 * @author dYdX
 *
 * Public function that allows other addresses to manage accounts
 */
contract Permission is
    HasState
{
    // ============ Events ============

    event LogOperatorSet(
        address indexed owner,
        address operator,
        bool trusted
    );

    // ============ Public Functions ============

    /**
     * Approves/disapproves any number of operators. An operator is an external address that has the
     * same permissions to manipulate an account as the owner of the account. Operators are simply
     * addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
     *
     * Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
     * operator is a smart contract and implements the IAutoTrader interface.
     *
     * @param  args  A list of OperatorArgs which have an address and a boolean. The boolean value
     *               denotes whether to approve (true) or revoke approval (false) for that address.
     */
    function setOperators(
        Types.OperatorArg[] memory args
    )
        public
    {
        uint256 length = args.length;
        for (uint256 i; i < length; ++i) {
            address operator = args[i].operator;
            bool trusted = args[i].trusted;
            g_state.operators[msg.sender][operator] = trusted ? 1 : 2;
            emit LogOperatorSet(msg.sender, operator, trusted);
        }
    }
}

File 46 of 50 : Operation.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import { OperationImpl } from "./impl/OperationImpl.sol";

import { Account } from "./lib/Account.sol";
import { Actions } from "./lib/Actions.sol";

import { HasState } from "./HasState.sol";


/**
 * @title Operation
 * @author dYdX
 *
 * Primary public function for allowing users and contracts to manage accounts within DolomiteMargin
 */
contract Operation is
    HasState,
    ReentrancyGuard
{
    // ============ Public Functions ============

    /**
     * The main entry-point to DolomiteMargin that allows users and contracts to manage accounts.
     * Take one or more actions on one or more accounts. The msg.sender must be the owner or
     * operator of all accounts except for those being liquidated, vaporized, or traded with.
     * One call to operate() is considered a singular "operation". Account collateralization is
     * ensured only after the completion of the entire operation.
     *
     * @param  accounts  A list of all accounts that will be used in this operation. Cannot contain
     *                   duplicates. In each action, the relevant account will be referred-to by its
     *                   index in the list.
     * @param  actions   An ordered list of all actions that will be taken in this operation. The
     *                   actions will be processed in order.
     */
    function operate(
        Account.Info[] memory accounts,
        Actions.ActionArgs[] memory actions
    )
        public
        nonReentrant
    {
        OperationImpl.operate(
            g_state,
            accounts,
            actions
        );
    }
}

File 47 of 50 : HasState.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Storage } from "./lib/Storage.sol";


/**
 * @title HasState
 * @author dYdX
 *
 * Base-level contract that holds the state of DolomiteMargin
 */
contract HasState
{
    Storage.State internal g_state;
}

File 48 of 50 : Getters.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { IAccountRiskOverrideSetter } from "./interfaces/IAccountRiskOverrideSetter.sol";
import { IDolomiteMargin } from "./interfaces/IDolomiteMargin.sol";
import { IInterestSetter } from "./interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "./interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "./interfaces/IPriceOracle.sol";

import { GettersImpl } from "./impl/GettersImpl.sol";

import { Account } from "./lib/Account.sol";
import { Decimal } from "./lib/Decimal.sol";
import { Interest } from "./lib/Interest.sol";
import { Monetary } from "./lib/Monetary.sol";
import { Storage } from "./lib/Storage.sol";
import { Types } from "./lib/Types.sol";

import { HasState } from "./HasState.sol";


/**
 * @title Getters
 * @author dYdX
 *
 * Public read-only functions that allow transparency into the state of DolomiteMargin
 */
contract Getters is
    IDolomiteMargin,
    HasState
{
    // ============ Getters for Risk ============

    function getMarginRatio()
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarginRatio(g_state);
    }

    function getMarginRatioForAccount(
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarginRatioForAccount(g_state, account);
    }

    function getLiquidationSpread()
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getLiquidationSpread(g_state);
    }

    function getLiquidationSpreadForPair(
        uint256 heldMarketId,
        uint256 owedMarketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        Account.Info memory account = Account.Info({
            owner: address(0),
            number: 0
        });
        return GettersImpl.getLiquidationSpreadForAccountAndPair(
            g_state,
            account,
            heldMarketId,
            owedMarketId
        );
    }

    function getLiquidationSpreadForAccountAndPair(
        Account.Info memory account,
        uint256 heldMarketId,
        uint256 owedMarketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getLiquidationSpreadForAccountAndPair(
            g_state,
            account,
            heldMarketId,
            owedMarketId
        );
    }

    function getEarningsRate()
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getEarningsRate(g_state);
    }

    function getMinBorrowedValue()
        public
        view
        returns (Monetary.Value memory)
    {
        return GettersImpl.getMinBorrowedValue(g_state);
    }

    function getAccountMaxNumberOfMarketsWithBalances()
        public
        view
        returns (uint256)
    {
        return GettersImpl.getAccountMaxNumberOfMarketsWithBalances(g_state);
    }

    function getOracleSentinel()
        public
        view
        returns (IOracleSentinel)
    {
        return GettersImpl.getOracleSentinel(g_state);
    }

    function getIsBorrowAllowed()
        public
        view
        returns (bool)
    {
        return GettersImpl.getIsBorrowAllowed(g_state);
    }

    function getIsLiquidationAllowed()
        public
        view
        returns (bool)
    {
        return GettersImpl.getIsLiquidationAllowed(g_state);
    }

    function getCallbackGasLimit()
        public
        view
        returns (uint256)
    {
        return GettersImpl.getCallbackGasLimit(g_state);
    }

    function getDefaultAccountRiskOverrideSetter()
        public
        view
        returns (IAccountRiskOverrideSetter)
    {
        return GettersImpl.getDefaultAccountRiskOverrideSetter(g_state);
    }

    function getAccountRiskOverrideSetterByAccountOwner(
        address accountOwner
    )
        public
        view
        returns (IAccountRiskOverrideSetter)
    {
        return GettersImpl.getAccountRiskOverrideSetterByAccountOwner(g_state, accountOwner);
    }

    function getAccountRiskOverrideByAccount(
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory marginRatioOverride, Decimal.D256 memory liquidationSpreadOverride)
    {
        (marginRatioOverride, liquidationSpreadOverride) =
            GettersImpl.getAccountRiskOverrideByAccount(g_state, account);
    }

    function getMarginRatioOverrideByAccount(
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarginRatioOverrideByAccount(g_state, account);
    }

    function getLiquidationSpreadOverrideByAccount(
        Account.Info memory account
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getLiquidationSpreadOverrideByAccount(g_state, account);
    }

    function getRiskLimits()
        public
        view
        returns (Storage.RiskLimits memory)
    {
        return GettersImpl.getRiskLimits(g_state);
    }

    // ============ Getters for Markets ============

    function getNumMarkets()
        public
        view
        returns (uint256)
    {
        return GettersImpl.getNumMarkets(g_state);
    }

    function getMarketIdByTokenAddress(
        address token
    )
        public
        view
        returns (uint256)
    {
        return GettersImpl.getMarketIdByTokenAddress(g_state, token);
    }

    function getMarketTokenAddress(
        uint256 marketId
    )
        public
        view
        returns (address)
    {
        return GettersImpl.getMarketTokenAddress(g_state, marketId);
    }

    function getMarketIsClosing(
        uint256 marketId
    )
        public
        view
        returns (bool)
    {
        return GettersImpl.getMarketIsClosing(g_state, marketId);
    }

    function getMarketPrice(
        uint256 marketId
    )
        public
        view
        returns (Monetary.Price memory)
    {
        return GettersImpl.getMarketPrice(g_state, marketId);
    }

    function getMarketTotalPar(
        uint256 marketId
    )
        public
        view
        returns (Types.TotalPar memory)
    {
        return GettersImpl.getMarketTotalPar(g_state, marketId);
    }

    function getMarketTotalWei(
        uint256 marketId
    )
        public
        view
        returns (Types.TotalWei memory)
    {
        return GettersImpl.getMarketTotalWei(g_state, marketId);
    }

    function getMarketCachedIndex(
        uint256 marketId
    )
        public
        view
        returns (Interest.Index memory)
    {
        return GettersImpl.getMarketCachedIndex(g_state, marketId);
    }

    function getMarketCurrentIndex(
        uint256 marketId
    )
        public
        view
        returns (Interest.Index memory)
    {
        return GettersImpl.getMarketCurrentIndex(g_state, marketId);
    }

    function getMarketPriceOracle(
        uint256 marketId
    )
        public
        view
        returns (IPriceOracle)
    {
        return GettersImpl.getMarketPriceOracle(g_state, marketId);
    }

    function getMarketInterestSetter(
        uint256 marketId
    )
        public
        view
        returns (IInterestSetter)
    {
        return GettersImpl.getMarketInterestSetter(g_state, marketId);
    }

    function getMarketMarginPremium(
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarketMarginPremium(g_state, marketId);
    }

    function getMarketLiquidationSpreadPremium(
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarketLiquidationSpreadPremium(g_state, marketId);
    }

    function getMarketSpreadPremium(
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarketLiquidationSpreadPremium(g_state, marketId);
    }

    function getMarketMaxWei(
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        return GettersImpl.getMarketMaxSupplyWei(g_state, marketId);
    }

    function getMarketMaxSupplyWei(
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        return GettersImpl.getMarketMaxSupplyWei(g_state, marketId);
    }

    function getMarketMaxBorrowWei(
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        return GettersImpl.getMarketMaxBorrowWei(g_state, marketId);
    }

    function getMarketEarningsRateOverride(
        uint256 marketId
    )
        public
        view
        returns (Decimal.D256 memory)
    {
        return GettersImpl.getMarketEarningsRateOverride(g_state, marketId);
    }

    function getMarketInterestRate(
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        return GettersImpl.getMarketBorrowInterestRatePerSecond(g_state, marketId);
    }

    function getMarketBorrowInterestRatePerSecond(
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        return GettersImpl.getMarketBorrowInterestRatePerSecond(g_state, marketId);
    }

    function getMarketBorrowInterestRateApr(
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        return GettersImpl.getMarketBorrowInterestRateApr(g_state, marketId);
    }

    function getMarketSupplyInterestRateApr(
        uint256 marketId
    )
        public
        view
        returns (Interest.Rate memory)
    {
        return GettersImpl.getMarketSupplyInterestRateApr(g_state, marketId);
    }

    function getMarket(
        uint256 marketId
    )
        public
        view
        returns (Storage.Market memory)
    {
        return GettersImpl.getMarket(g_state, marketId);
    }

    function getMarketWithInfo(
        uint256 marketId
    )
        public
        view
        returns (
            Storage.Market memory,
            Interest.Index memory,
            Monetary.Price memory,
            Interest.Rate memory
        )
    {
        return GettersImpl.getMarketWithInfo(g_state, marketId);
    }

    function getNumExcessTokens(
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        return GettersImpl.getNumExcessTokens(g_state, marketId);
    }

    // ============ Getters for Accounts ============

    function getAccountPar(
        Account.Info memory account,
        uint256 marketId
    )
        public
        view
        returns (Types.Par memory)
    {
        return GettersImpl.getAccountPar(g_state, account, marketId);
    }

    function getAccountWei(
        Account.Info memory account,
        uint256 marketId
    )
        public
        view
        returns (Types.Wei memory)
    {
        return GettersImpl.getAccountWei(g_state, account, marketId);
    }

    function getAccountStatus(
        Account.Info memory account
    )
        public
        view
        returns (Account.Status)
    {
        return GettersImpl.getAccountStatus(g_state, account);
    }

    function getAccountMarketsWithBalances(
        Account.Info memory account
    )
        public
        view
        returns (uint256[] memory)
    {
        return GettersImpl.getAccountMarketsWithBalances(g_state, account);
    }

    function getAccountNumberOfMarketsWithBalances(
        Account.Info memory account
    )
        public
        view
        returns (uint256)
    {
        return GettersImpl.getAccountNumberOfMarketsWithBalances(g_state, account);
    }

    function getAccountMarketWithBalanceAtIndex(
        Account.Info memory account,
        uint256 index
    )
        public
        view
        returns (uint256)
    {
        return GettersImpl.getAccountMarketWithBalanceAtIndex(g_state, account, index);
    }

    function getAccountNumberOfMarketsWithDebt(
        Account.Info memory account
    )
        public
        view
        returns (uint256)
    {
        return GettersImpl.getAccountNumberOfMarketsWithDebt(g_state, account);
    }

    function getAccountValues(
        Account.Info memory account
    )
        public
        view
        returns (Monetary.Value memory, Monetary.Value memory)
    {
        return GettersImpl.getAccountValues(g_state, account);
    }

    function getAdjustedAccountValues(
        Account.Info memory account
    )
        public
        view
        returns (Monetary.Value memory, Monetary.Value memory)
    {
        return GettersImpl.getAdjustedAccountValues(g_state, account);
    }

    function getAccountBalances(
        Account.Info memory account
    )
        public
        view
        returns (
            uint[] memory,
            address[] memory,
            Types.Par[] memory,
            Types.Wei[] memory
        )
    {
        return GettersImpl.getAccountBalances(g_state, account);
    }

    // ============ Getters for Account Permissions ============

    function getIsLocalOperator(
        address owner,
        address operator
    )
        public
        view
        returns (bool)
    {
        return GettersImpl.getIsLocalOperator(g_state, owner, operator);
    }

    function getIsGlobalOperator(
        address operator
    )
        public
        view
        returns (bool)
    {
        return GettersImpl.getIsGlobalOperator(g_state, operator);
    }

    function getIsAutoTraderSpecial(
        address autoTrader
    )
        public
        view
        returns (bool)
    {
        return GettersImpl.getIsAutoTraderSpecial(g_state, autoTrader);
    }
}

File 49 of 50 : ReentrancyGuard.sol
pragma solidity ^0.5.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 *
 * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
 * metering changes introduced in the Istanbul hardfork.
 */
contract ReentrancyGuard {
    bool private _notEntered;

    constructor () internal {
        // Storing an initial non-zero value makes deployment a bit more
        // expensive, but in exchange the refund on every call to nonReentrant
        // will be lower in amount. Since refunds are capped to a percetange of
        // the total transaction's gas, it is best to keep them low in cases
        // like this one, to increase the likelihood of the full refund coming
        // into effect.
        _notEntered = true;
    }

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

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

        _;

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

File 50 of 50 : Admin.sol
/*

    Copyright 2019 dYdX Trading Inc.

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

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

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

*/

pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;

import { Ownable } from "@openzeppelin/contracts/ownership/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import { IAccountRiskOverrideSetter } from "./interfaces/IAccountRiskOverrideSetter.sol";
import { IDolomiteMargin } from "./interfaces/IDolomiteMargin.sol";
import { IInterestSetter } from "./interfaces/IInterestSetter.sol";
import { IOracleSentinel } from "./interfaces/IOracleSentinel.sol";
import { IPriceOracle } from "./interfaces/IPriceOracle.sol";

import { AdminImpl } from "./impl/AdminImpl.sol";

import { Decimal } from "./lib/Decimal.sol";
import { Interest } from "./lib/Interest.sol";
import { Monetary } from "./lib/Monetary.sol";
import { Token } from "./lib/Token.sol";

import { HasState } from "./HasState.sol";


/**
 * @title Admin
 * @author dYdX
 *
 * Public functions that allow the privileged owner address to manage DolomiteMargin
 */
contract Admin is
    IDolomiteMargin,
    HasState,
    Ownable,
    ReentrancyGuard
{
    // ============ Token Functions ============

    function ownerWithdrawExcessTokens(
        uint256 marketId,
        address recipient
    )
        public
        onlyOwner
        nonReentrant
        returns (uint256)
    {
        return AdminImpl.ownerWithdrawExcessTokens(
            g_state,
            marketId,
            recipient
        );
    }

    function ownerWithdrawUnsupportedTokens(
        address token,
        address recipient
    )
        public
        onlyOwner
        nonReentrant
        returns (uint256)
    {
        return AdminImpl.ownerWithdrawUnsupportedTokens(
            g_state,
            token,
            recipient
        );
    }

    // ============ Market Functions ============

    function ownerAddMarket(
        address token,
        IPriceOracle priceOracle,
        IInterestSetter interestSetter,
        Decimal.D256 memory marginPremium,
        Decimal.D256 memory spreadPremium,
        uint256 maxSupplyWei,
        uint256 maxBorrowWei,
        Decimal.D256 memory earningsRateOverride,
        bool isClosing
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerAddMarket(
            g_state,
            token,
            priceOracle,
            interestSetter,
            marginPremium,
            spreadPremium,
            maxSupplyWei,
            maxBorrowWei,
            earningsRateOverride,
            isClosing
        );
    }

    function ownerSetIsClosing(
        uint256 marketId,
        bool isClosing
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetIsClosing(
            g_state,
            marketId,
            isClosing
        );
    }

    function ownerSetPriceOracle(
        uint256 marketId,
        IPriceOracle priceOracle
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetPriceOracle(
            g_state,
            marketId,
            priceOracle
        );
    }

    function ownerSetInterestSetter(
        uint256 marketId,
        IInterestSetter interestSetter
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetInterestSetter(
            g_state,
            marketId,
            interestSetter
        );
    }

    function ownerSetMarginPremium(
        uint256 marketId,
        Decimal.D256 memory marginPremium
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetMarginPremium(
            g_state,
            marketId,
            marginPremium
        );
    }

    function ownerSetLiquidationSpreadPremium(
        uint256 marketId,
        Decimal.D256 memory liquidationSpreadPremium
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetLiquidationSpreadPremium(
            g_state,
            marketId,
            liquidationSpreadPremium
        );
    }

    function ownerSetMaxSupplyWei(
        uint256 marketId,
        uint256 maxSupplyWei
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetMaxSupplyWei(
            g_state,
            marketId,
            maxSupplyWei
        );
    }

    function ownerSetMaxBorrowWei(
        uint256 marketId,
        uint256 maxBorrowWei
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetMaxBorrowWei(
            g_state,
            marketId,
            maxBorrowWei
        );
    }

    function ownerSetEarningsRateOverride(
        uint256 marketId,
        Decimal.D256 memory earningsRateOverride
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetEarningsRateOverride(
            g_state,
            marketId,
            earningsRateOverride
        );
    }

    // ============ Risk Functions ============

    function ownerSetMarginRatio(
        Decimal.D256 memory ratio
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetMarginRatio(
            g_state,
            ratio
        );
    }

    function ownerSetLiquidationSpread(
        Decimal.D256 memory spread
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetLiquidationSpread(
            g_state,
            spread
        );
    }

    function ownerSetEarningsRate(
        Decimal.D256 memory earningsRate
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetEarningsRate(
            g_state,
            earningsRate
        );
    }

    function ownerSetMinBorrowedValue(
        Monetary.Value memory minBorrowedValue
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetMinBorrowedValue(
            g_state,
            minBorrowedValue
        );
    }

    function ownerSetAccountMaxNumberOfMarketsWithBalances(
        uint256 accountMaxNumberOfMarketsWithBalances
    )
    public
    onlyOwner
    nonReentrant
    {
        AdminImpl.ownerSetAccountMaxNumberOfMarketsWithBalances(
            g_state,
            accountMaxNumberOfMarketsWithBalances
        );
    }

    function ownerSetOracleSentinel(
        IOracleSentinel oracleSentinel
    )
    public
    onlyOwner
    nonReentrant
    {
        AdminImpl.ownerSetOracleSentinel(g_state, oracleSentinel);
    }

    function ownerSetCallbackGasLimit(
        uint256 callbackGasLimit
    )
    public
    onlyOwner
    nonReentrant
    {
        AdminImpl.ownerSetCallbackGasLimit(g_state, callbackGasLimit);
    }

    function ownerSetDefaultAccountRiskOverride(
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    )
    public
    onlyOwner
    nonReentrant
    {
        AdminImpl.ownerSetDefaultAccountRiskOverride(g_state, accountRiskOverrideSetter);
    }

    function ownerSetAccountRiskOverride(
        address accountOwner,
        IAccountRiskOverrideSetter accountRiskOverrideSetter
    )
    public
    onlyOwner
    nonReentrant
    {
        AdminImpl.ownerSetAccountRiskOverride(g_state, accountOwner, accountRiskOverrideSetter);
    }

    // ============ Global Operator Functions ============

    function ownerSetGlobalOperator(
        address operator,
        bool approved
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetGlobalOperator(
            g_state,
            operator,
            approved
        );
    }

    function ownerSetAutoTraderSpecial(
        address autoTrader,
        bool special
    )
        public
        onlyOwner
        nonReentrant
    {
        AdminImpl.ownerSetAutoTraderSpecial(
            g_state,
            autoTrader,
            special
        );
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "evmVersion": "istanbul",
  "libraries": {
    "/home/cdc218/projects/dolomite-protocol-v2/contracts/protocol/impl/AdminImpl.sol": {
      "AdminImpl": "0x43C2FDB89A1C491F9FE86E1Ff05bd2BE204Ab4aE"
    },
    "/home/cdc218/projects/dolomite-protocol-v2/contracts/protocol/impl/GettersImpl.sol": {
      "GettersImpl": "0x6B15c3F0D266be946eA2d33C40d1a7204C8403a2"
    },
    "/home/cdc218/projects/dolomite-protocol-v2/contracts/protocol/impl/OperationImpl.sol": {
      "OperationImpl": "0xe4d3450d52EDF515433FEc12eaEFFFBfa83250b9"
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"uint64","name":"marginRatioMax","type":"uint64"},{"internalType":"uint64","name":"liquidationSpreadMax","type":"uint64"},{"internalType":"uint64","name":"earningsRateMax","type":"uint64"},{"internalType":"uint64","name":"marginPremiumMax","type":"uint64"},{"internalType":"uint64","name":"liquidationSpreadPremiumMax","type":"uint64"},{"internalType":"uint96","name":"interestRateMax","type":"uint96"},{"internalType":"uint128","name":"minBorrowedValueMax","type":"uint128"}],"internalType":"struct Storage.RiskLimits","name":"riskLimits","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginRatio","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"liquidationSpread","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRate","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"minBorrowedValue","type":"tuple"},{"internalType":"uint256","name":"accountMaxNumberOfMarketsWithBalances","type":"uint256"},{"internalType":"contract IOracleSentinel","name":"oracleSentinel","type":"address"},{"internalType":"uint256","name":"callbackGasLimit","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"trusted","type":"bool"}],"name":"LogOperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountBalances","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint128","name":"value","type":"uint128"}],"internalType":"struct Types.Par[]","name":"","type":"tuple[]"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei[]","name":"","type":"tuple[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getAccountMarketWithBalanceAtIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountMarketsWithBalances","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAccountMaxNumberOfMarketsWithBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountNumberOfMarketsWithBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountNumberOfMarketsWithDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getAccountPar","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint128","name":"value","type":"uint128"}],"internalType":"struct Types.Par","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountRiskOverrideByAccount","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginRatioOverride","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"liquidationSpreadOverride","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"getAccountRiskOverrideSetterByAccountOwner","outputs":[{"internalType":"contract IAccountRiskOverrideSetter","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountStatus","outputs":[{"internalType":"enum Account.Status","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAccountValues","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getAccountWei","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getAdjustedAccountValues","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCallbackGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDefaultAccountRiskOverrideSetter","outputs":[{"internalType":"contract IAccountRiskOverrideSetter","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEarningsRate","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"autoTrader","type":"address"}],"name":"getIsAutoTraderSpecial","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIsBorrowAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getIsGlobalOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIsLiquidationAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"getIsLocalOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLiquidationSpread","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"},{"internalType":"uint256","name":"heldMarketId","type":"uint256"},{"internalType":"uint256","name":"owedMarketId","type":"uint256"}],"name":"getLiquidationSpreadForAccountAndPair","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"heldMarketId","type":"uint256"},{"internalType":"uint256","name":"owedMarketId","type":"uint256"}],"name":"getLiquidationSpreadForPair","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getLiquidationSpreadOverrideByAccount","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMarginRatio","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getMarginRatioForAccount","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"account","type":"tuple"}],"name":"getMarginRatioOverrideByAccount","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarket","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isClosing","type":"bool"},{"components":[{"internalType":"uint128","name":"borrow","type":"uint128"},{"internalType":"uint128","name":"supply","type":"uint128"}],"internalType":"struct Types.TotalPar","name":"totalPar","type":"tuple"},{"components":[{"internalType":"uint112","name":"borrow","type":"uint112"},{"internalType":"uint112","name":"supply","type":"uint112"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"internalType":"struct Interest.Index","name":"index","type":"tuple"},{"internalType":"contract IPriceOracle","name":"priceOracle","type":"address"},{"internalType":"contract IInterestSetter","name":"interestSetter","type":"address"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginPremium","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"liquidationSpreadPremium","type":"tuple"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"maxSupplyWei","type":"tuple"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"maxBorrowWei","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRateOverride","type":"tuple"}],"internalType":"struct Storage.Market","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketBorrowInterestRateApr","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Interest.Rate","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketBorrowInterestRatePerSecond","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Interest.Rate","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketCachedIndex","outputs":[{"components":[{"internalType":"uint112","name":"borrow","type":"uint112"},{"internalType":"uint112","name":"supply","type":"uint112"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"internalType":"struct Interest.Index","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketCurrentIndex","outputs":[{"components":[{"internalType":"uint112","name":"borrow","type":"uint112"},{"internalType":"uint112","name":"supply","type":"uint112"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"internalType":"struct Interest.Index","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketEarningsRateOverride","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getMarketIdByTokenAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketInterestRate","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Interest.Rate","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketInterestSetter","outputs":[{"internalType":"contract IInterestSetter","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketIsClosing","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketLiquidationSpreadPremium","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketMarginPremium","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketMaxBorrowWei","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketMaxSupplyWei","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketMaxWei","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketPrice","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Price","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketPriceOracle","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketSpreadPremium","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketSupplyInterestRateApr","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Interest.Rate","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketTotalPar","outputs":[{"components":[{"internalType":"uint128","name":"borrow","type":"uint128"},{"internalType":"uint128","name":"supply","type":"uint128"}],"internalType":"struct Types.TotalPar","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketTotalWei","outputs":[{"components":[{"internalType":"uint128","name":"borrow","type":"uint128"},{"internalType":"uint128","name":"supply","type":"uint128"}],"internalType":"struct Types.TotalWei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getMarketWithInfo","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isClosing","type":"bool"},{"components":[{"internalType":"uint128","name":"borrow","type":"uint128"},{"internalType":"uint128","name":"supply","type":"uint128"}],"internalType":"struct Types.TotalPar","name":"totalPar","type":"tuple"},{"components":[{"internalType":"uint112","name":"borrow","type":"uint112"},{"internalType":"uint112","name":"supply","type":"uint112"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"internalType":"struct Interest.Index","name":"index","type":"tuple"},{"internalType":"contract IPriceOracle","name":"priceOracle","type":"address"},{"internalType":"contract IInterestSetter","name":"interestSetter","type":"address"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginPremium","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"liquidationSpreadPremium","type":"tuple"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"maxSupplyWei","type":"tuple"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"maxBorrowWei","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRateOverride","type":"tuple"}],"internalType":"struct Storage.Market","name":"","type":"tuple"},{"components":[{"internalType":"uint112","name":"borrow","type":"uint112"},{"internalType":"uint112","name":"supply","type":"uint112"},{"internalType":"uint32","name":"lastUpdate","type":"uint32"}],"internalType":"struct Interest.Index","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Price","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Interest.Rate","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMinBorrowedValue","outputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getNumExcessTokens","outputs":[{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.Wei","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNumMarkets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOracleSentinel","outputs":[{"internalType":"contract IOracleSentinel","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRiskLimits","outputs":[{"components":[{"internalType":"uint64","name":"marginRatioMax","type":"uint64"},{"internalType":"uint64","name":"liquidationSpreadMax","type":"uint64"},{"internalType":"uint64","name":"earningsRateMax","type":"uint64"},{"internalType":"uint64","name":"marginPremiumMax","type":"uint64"},{"internalType":"uint64","name":"liquidationSpreadPremiumMax","type":"uint64"},{"internalType":"uint96","name":"interestRateMax","type":"uint96"},{"internalType":"uint128","name":"minBorrowedValueMax","type":"uint128"}],"internalType":"struct Storage.RiskLimits","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info[]","name":"accounts","type":"tuple[]"},{"components":[{"internalType":"enum Actions.ActionType","name":"actionType","type":"uint8"},{"internalType":"uint256","name":"accountId","type":"uint256"},{"components":[{"internalType":"bool","name":"sign","type":"bool"},{"internalType":"enum Types.AssetDenomination","name":"denomination","type":"uint8"},{"internalType":"enum Types.AssetReference","name":"ref","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Types.AssetAmount","name":"amount","type":"tuple"},{"internalType":"uint256","name":"primaryMarketId","type":"uint256"},{"internalType":"uint256","name":"secondaryMarketId","type":"uint256"},{"internalType":"address","name":"otherAddress","type":"address"},{"internalType":"uint256","name":"otherAccountId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Actions.ActionArgs[]","name":"actions","type":"tuple[]"}],"name":"operate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IPriceOracle","name":"priceOracle","type":"address"},{"internalType":"contract IInterestSetter","name":"interestSetter","type":"address"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginPremium","type":"tuple"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"spreadPremium","type":"tuple"},{"internalType":"uint256","name":"maxSupplyWei","type":"uint256"},{"internalType":"uint256","name":"maxBorrowWei","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRateOverride","type":"tuple"},{"internalType":"bool","name":"isClosing","type":"bool"}],"name":"ownerAddMarket","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"accountMaxNumberOfMarketsWithBalances","type":"uint256"}],"name":"ownerSetAccountMaxNumberOfMarketsWithBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"contract IAccountRiskOverrideSetter","name":"accountRiskOverrideSetter","type":"address"}],"name":"ownerSetAccountRiskOverride","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"autoTrader","type":"address"},{"internalType":"bool","name":"special","type":"bool"}],"name":"ownerSetAutoTraderSpecial","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"callbackGasLimit","type":"uint256"}],"name":"ownerSetCallbackGasLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IAccountRiskOverrideSetter","name":"accountRiskOverrideSetter","type":"address"}],"name":"ownerSetDefaultAccountRiskOverride","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRate","type":"tuple"}],"name":"ownerSetEarningsRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"earningsRateOverride","type":"tuple"}],"name":"ownerSetEarningsRateOverride","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"ownerSetGlobalOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"contract IInterestSetter","name":"interestSetter","type":"address"}],"name":"ownerSetInterestSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"bool","name":"isClosing","type":"bool"}],"name":"ownerSetIsClosing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"spread","type":"tuple"}],"name":"ownerSetLiquidationSpread","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"liquidationSpreadPremium","type":"tuple"}],"name":"ownerSetLiquidationSpreadPremium","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"marginPremium","type":"tuple"}],"name":"ownerSetMarginPremium","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Decimal.D256","name":"ratio","type":"tuple"}],"name":"ownerSetMarginRatio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"uint256","name":"maxBorrowWei","type":"uint256"}],"name":"ownerSetMaxBorrowWei","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"uint256","name":"maxSupplyWei","type":"uint256"}],"name":"ownerSetMaxSupplyWei","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Monetary.Value","name":"minBorrowedValue","type":"tuple"}],"name":"ownerSetMinBorrowedValue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IOracleSentinel","name":"oracleSentinel","type":"address"}],"name":"ownerSetOracleSentinel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"contract IPriceOracle","name":"priceOracle","type":"address"}],"name":"ownerSetPriceOracle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"ownerWithdrawExcessTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"ownerWithdrawUnsupportedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"trusted","type":"bool"}],"internalType":"struct Types.OperatorArg[]","name":"args","type":"tuple[]"}],"name":"setOperators","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162006156380380620061568339810160408190526200003491620005d0565b6000620000496001600160e01b036200049116565b601380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35060138054600160a01b60ff60a01b1990911617905587516010805460208b01516040808d015160608e01516001600160401b03199485166001600160401b0397881617600160401b600160801b03191668010000000000000000948816850217600160801b600160c01b031916600160801b92881692909202919091176001600160c01b0316600160c01b918716919091021790935560808c01516011805460a08f015194169190951617600160401b600160a01b0319166001600160601b03909216021790915560c0890151601280546001600160801b0319166001600160801b0390921691909117905551632a6e9a8b60e11b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906354dd351690620001bd906000908b90600401620006f6565b60006040518083038186803b158015620001d657600080fd5b505af4158015620001eb573d6000803e3d6000fd5b50506040516303d139af60e21b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9250630f44e6bc91506200022b906000908a90600401620006f6565b60006040518083038186803b1580156200024457600080fd5b505af415801562000259573d6000803e3d6000fd5b5050604051630311401d60e41b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae925063311401d0915062000299906000908990600401620006f6565b60006040518083038186803b158015620002b257600080fd5b505af4158015620002c7573d6000803e3d6000fd5b5050604051633e72b31360e21b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae925063f9cacc4c915062000307906000908890600401620006f6565b60006040518083038186803b1580156200032057600080fd5b505af415801562000335573d6000803e3d6000fd5b5050604051631c2d854760e21b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae92506370b6151c91506200037590600090879060040162000715565b60006040518083038186803b1580156200038e57600080fd5b505af4158015620003a3573d6000803e3d6000fd5b5050604051632e5c853560e11b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9250635cb90a6a9150620003e3906000908690600401620006d0565b60006040518083038186803b158015620003fc57600080fd5b505af415801562000411573d6000803e3d6000fd5b50506040516301cf9adf60e31b81527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9250630e7cd6f891506200045190600090859060040162000715565b60006040518083038186803b1580156200046a57600080fd5b505af41580156200047f573d6000803e3d6000fd5b505050505050505050505050620007ee565b3390565b8051620004a281620007a8565b92915050565b600060208284031215620004bb57600080fd5b620004c7602062000734565b90506000620004d78484620005a9565b82525092915050565b600060e08284031215620004f357600080fd5b620004ff60e062000734565b905060006200050f8484620005b6565b82525060206200052284848301620005b6565b60208301525060406200053884828501620005b6565b60408301525060606200054e84828501620005b6565b60608301525060806200056484828501620005b6565b60808301525060a06200057a84828501620005c3565b60a08301525060c062000590848285016200059c565b60c08301525092915050565b8051620004a281620007c2565b8051620004a281620007cd565b8051620004a281620007d8565b8051620004a281620007e3565b6000806000806000806000806101c0898b031215620005ee57600080fd5b6000620005fc8b8b620004e0565b98505060e06200060f8b828c01620004a8565b975050610100620006238b828c01620004a8565b965050610120620006378b828c01620004a8565b9550506101406200064b8b828c01620004a8565b9450506101606200065f8b828c01620005a9565b935050610180620006738b828c0162000495565b9250506101a0620006878b828c01620005a9565b9150509295985092959890939650565b620006a28162000768565b82525050565b80516020830190620006bb8482620006c5565b50505050565b9052565b620006a2816200078d565b60408101620006e08285620006c1565b620006ef602083018462000697565b9392505050565b60408101620007068285620006c1565b620006ef6020830184620006a8565b60408101620007258285620006c1565b620006ef6020830184620006c5565b6040518181016001600160401b03811182821017156200075357600080fd5b604052919050565b6000620004a28262000781565b6000620004a2826200075b565b6001600160801b031690565b6001600160a01b031690565b90565b6001600160401b031690565b6001600160601b031690565b620007b38162000768565b8114620007bf57600080fd5b50565b620007b38162000775565b620007b3816200078d565b620007b38162000790565b620007b3816200079c565b61595880620007fe6000396000f3fe608060405234801561001057600080fd5b50600436106105ac5760003560e01c80638da5cb5b116102ed578063deec053d11610191578063ec6e3987116100ee578063f2385be3116100a2578063f94160521161007c578063f941605214610bcb578063fd04b60614610bde578063fd47eda614610956576105ac565b8063f2385be314610b9d578063f2901ae214610ba5578063f2fde38b14610bb8576105ac565b8063ed4d6f5c116100d3578063ed4d6f5c14610b64578063ef6957d014610b77578063f1061ba814610b8a576105ac565b8063ec6e398714610b3e578063ecef481f14610b51576105ac565b8063e56659fc11610145578063e8e72f751161012a578063e8e72f7514610af6578063eb1c6e6b14610b09578063eb44fdd314610b1e576105ac565b8063e56659fc14610ad0578063e5e23ef214610ae3576105ac565b8063e51bfcb411610176578063e51bfcb414610a95578063e52828c714610ab5578063e552022814610ac8576105ac565b8063deec053d14610a6f578063e0e48d1914610a82576105ac565b8063ba77ea671161024a578063cb04a34c116101fe578063d93c9591116101d8578063d93c959114610796578063dc6d784614610a54578063de8be8e714610a67576105ac565b8063cb04a34c14610a0e578063d24c48bc14610a2e578063d5ecf7c514610a41576105ac565b8063c0e044fc1161022f578063c0e044fc146109eb578063c1460942146109f3578063c190c2ec146109fb576105ac565b8063ba77ea67146109c5578063c0bb72b7146109d8576105ac565b8063ac7bb20b116102a1578063b306e4f811610286578063b306e4f81461097c578063b548b8921461098f578063b64e31ea146109b2576105ac565b8063ac7bb20b14610956578063b0dc49f814610969576105ac565b80638f6bc659116102d25780638f6bc6591461091d5780638fae3be114610930578063a67a6a4514610943576105ac565b80638da5cb5b1461090d5780638f32d59b14610915576105ac565b8063384027621161045457806356ea84b2116103b15780636a8194e7116103655780637e9eaf411161033f5780637e9eaf41146108df57806385b53fc8146108e75780638928378e146108fa576105ac565b80636a8194e7146108ac578063715018a6146108cf578063770e43e0146108d7576105ac565b80635d3c0ce8116103965780635d3c0ce81461087357806363ae56b31461088657806366411ff414610899576105ac565b806356ea84b2146108405780635ac7d17c14610860576105ac565b80634be87414116104085780634e634067116103ed5780634e6340671461081d5780634f3c1542146108255780634f4d66f61461082d576105ac565b80634be87414146108025780634db90d5214610815576105ac565b80633a031bf0116104395780633a031bf0146107bc57806347d1b53c146107cf57806348e648ee146107ef576105ac565b80633840276214610796578063387a498a146107a9576105ac565b806317b505251161050d57806320945e26116104c15780632e822af31161049b5780632e822af3146107705780633063bce2146107835780633305e5bb14610709576105ac565b806320945e2614610742578063295c39a5146107555780632a5608451461075d576105ac565b80631a7777bb116104f25780631a7777bb146107095780631ae4ec521461071c5780631eb120761461072f576105ac565b806317b50525146106d6578063197f0f05146106f6576105ac565b80630f47fab011610564578063121fb72f11610549578063121fb72f14610682578063124f914c1461069557806313368364146106b6576105ac565b80630f47fab014610642578063105de10c14610662576105ac565b80630781d820116105955780630781d820146105fa57806307af15681461060f5780630b28fa3e1461062f576105ac565b8063052f72d7146105b1578063062bd3e9146105da575b600080fd5b6105c46105bf3660046144af565b610bf1565b6040516105d191906152c8565b60405180910390f35b6105ed6105e8366004614a5f565b610c9e565b6040516105d19190615237565b61060d610608366004614a5f565b610d43565b005b61062261061d366004614a5f565b610e3f565b6040516105d19190615692565b61060d61063d366004614af8565b610ee9565b6106556106503660046148d4565b610fdf565b6040516105d19190615260565b6106756106703660046148d4565b6110a7565b6040516105d19190615322565b61060d610690366004614ad9565b611152565b6106a86106a33660046148d4565b611201565b6040516105d1929190615330565b6106c96106c4366004614a5f565b6112bc565b6040516105d191906152d6565b6106e96106e43660046148d4565b611361565b6040516105d191906156a0565b6106c9610704366004614a5f565b611406565b610675610717366004614a5f565b61145b565b61060d61072a366004614b28565b6114b6565b61067561073d366004614922565b611565565b6106756107503660046148d4565b61161e565b6106e9611679565b6106e961076b3660046144eb565b611721565b61060d61077e36600461484a565b61183d565b61060d61079136600461484a565b6118ea565b6106226107a4366004614a5f565b611997565b61060d6107b736600461484a565b6119f2565b6105c46107ca3660046144eb565b611a9f565b6107e26107dd3660046148f2565b611b4f565b6040516105d191906153a1565b6106c96107fd3660046144af565b611bfb565b61060d610810366004614af8565b611c50565b6105c4611cff565b6105c4611da2565b610675611df5565b6106e961083b3660046148d4565b611e9e565b61085361084e366004614a5f565b611ef3565b6040516105d1919061534b565b6105c461086e366004614a5f565b611f9e565b6106756108813660046148d4565b611ff3565b61060d610894366004614a5f565b61204e565b61060d6108a73660046147f0565b6120fb565b6108bf6108ba3660046148d4565b6121a8565b6040516105d19493929190615271565b61060d612269565b6106c96122fc565b61067561239f565b61060d6108f53660046146a8565b6123f8565b610675610908366004614a5f565b6124cf565b6105ed61252a565b6105c4612546565b6106e961092b366004614a9b565b612586565b6106e961093e3660046144af565b612637565b61060d610951366004614649565b61268c565b610675610964366004614a5f565b612717565b61060d610977366004614555565b612772565b61060d61098a366004614b28565b612821565b6109a261099d366004614a5f565b6128d0565b6040516105d19493929190615368565b6106756109c0366004614a5f565b612994565b6106a86109d33660046148d4565b6129ef565b61060d6109e636600461484a565b612aab565b6106e9612b58565b610675612bab565b610622610a093660046148f2565b612c04565b610a21610a1c366004614a5f565b612cb0565b6040516105d19190615684565b610675610a3c366004614b28565b612d5a565b610622610a4f366004614a5f565b612e2d565b610a21610a62366004614a5f565b612e88565b6106e9612ee3565b610853610a7d366004614a5f565b612f36565b6106e9610a903660046148f2565b612f91565b610aa8610aa33660046148d4565b613038565b6040516105d191906152e4565b610675610ac3366004614a5f565b6130dd565b610675613138565b61060d610ade3660046147f0565b613191565b61060d610af1366004614af8565b61323e565b61060d610b04366004614ad9565b6132ed565b610b1161339c565b6040516105d191906153af565b610b31610b2c366004614a5f565b613445565b6040516105d19190615359565b61060d610b4c366004614525565b6134f1565b610675610b5f366004614a5f565b6135a0565b61060d610b72366004614585565b6135fb565b61060d610b85366004614aba565b613706565b6105c4610b983660046144af565b6137b5565b6106c961380a565b61060d610bb3366004614525565b61385d565b61060d610bc63660046144af565b61390c565b6106a8610bd93660046148d4565b61393c565b610675610bec366004614a5f565b61399f565b6040517f288afc62000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063288afc6290610c4690849086906004016153cb565b60206040518083038186803b158015610c5e57600080fd5b505af4158015610c72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906147d2565b90505b919050565b6040517fa2169a3a000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a2169a3a90610cf390849086906004016155c8565b60206040518083038186803b158015610d0b57600080fd5b505af4158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906144cd565b610d4b612546565b610d705760405162461bcd60e51b8152600401610d6790615302565b60405180910390fd5b601354600160a01b900460ff16610d995760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f70b6151c0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906370b6151c90610df99060009085906004016155c8565b60006040518083038186803b158015610e1157600080fd5b505af4158015610e25573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b179055505050565b610e47613abf565b6040517f7462482a000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290637462482a90610e9a9060009086906004016155c8565b604080518083038186803b158015610eb157600080fd5b505af4158015610ec5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a41565b610ef1612546565b610f0d5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16610f365760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f1c90d8240000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90631c90d82490610f989060009086908690600401615634565b60006040518083038186803b158015610fb057600080fd5b505af4158015610fc4573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b17905550505050565b6040517fbbf70398000000000000000000000000000000000000000000000000000000008152606090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063bbf7039890611035906000908690600401615550565b60006040518083038186803b15801561104d57600080fd5b505af4158015611061573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c9691908101906146dd565b6110af613ad6565b6040517fc61f66b4000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063c61f66b490611102906000908690600401615550565b60206040518083038186803b15801561111a57600080fd5b505af415801561112e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614868565b61115a612546565b6111765760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661119f5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517faa92e3480000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063aa92e34890610f989060009086908690600401615619565b611209613ad6565b611211613ad6565b6040517fddc7c6f0000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ddc7c6f090611264906000908790600401615550565b604080518083038186803b15801561127b57600080fd5b505af415801561128f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112b39190810190614886565b91509150915091565b6040517fa18862fc000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a18862fc9061131190849086906004016155c8565b60206040518083038186803b15801561132957600080fd5b505af415801561133d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061480e565b6040517f6a0e6433000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290636a0e6433906113b69084908690600401615550565b60206040518083038186803b1580156113ce57600080fd5b505af41580156113e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a7d565b6040517f35b5d1f6000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906335b5d1f69061131190849086906004016155c8565b611463613ad6565b6040517f063bf373000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063063bf373906111029060009086906004016155c8565b6114be612546565b6114da5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166115035760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f3ea418830000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90633ea4188390610f98906000908690869060040161565c565b61156d613ad6565b6040517ffef3d3cb000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063fef3d3cb906115c490600090889088908890600401615593565b60206040518083038186803b1580156115dc57600080fd5b505af41580156115f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116149190810190614868565b90505b9392505050565b611626613ad6565b6040517f62f8e1b3000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906362f8e1b390611102906000908690600401615550565b6040517fe99388d3000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063e99388d3906116cc9084906004016153bd565b60206040518083038186803b1580156116e457600080fd5b505af41580156116f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614a7d565b905090565b600061172b612546565b6117475760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166117705760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fdd32998a0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063dd32998a906117d290600090879087906004016153e6565b60206040518083038186803b1580156117ea57600080fd5b505af41580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118229190810190614a7d565b90506013805460ff60a01b1916600160a01b17905592915050565b611845612546565b6118615760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661188a5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517ff9cacc4c0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063f9cacc4c90610df9906000908590600401615542565b6118f2612546565b61190e5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166119375760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f0f44e6bc0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90630f44e6bc90610df9906000908590600401615542565b61199f613abf565b6040517fc641ccd1000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063c641ccd190610e9a9060009086906004016155c8565b6119fa612546565b611a165760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16611a3f5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f311401d00000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063311401d090610df9906000908590600401615542565b6040517f75bcaa48000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906375bcaa4890611af6908490879087906004016153e6565b60206040518083038186803b158015611b0e57600080fd5b505af4158015611b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4691908101906147d2565b90505b92915050565b611b57613abf565b6040517f787f7727000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063787f772790611bac906000908790879060040161556b565b604080518083038186803b158015611bc357600080fd5b505af4158015611bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4691908101906149e7565b6040517f2e19a6ea000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290632e19a6ea9061131190849086906004016153cb565b611c58612546565b611c745760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16611c9d5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f7335079b0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90637335079b90610f989060009086908690600401615634565b6040517f83fe9e14000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906383fe9e1490611d529084906004016153bd565b60206040518083038186803b158015611d6a57600080fd5b505af4158015611d7e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c91908101906147d2565b6040517f6861138d000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290636861138d90611d529084906004016153bd565b611dfd613ad6565b6040517f4f820bd6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290634f820bd690611e4e906000906004016153bd565b60206040518083038186803b158015611e6657600080fd5b505af4158015611e7a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614868565b6040517fe647a687000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063e647a687906113b69084908690600401615550565b611efb613ae9565b6040517fd23406b6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d23406b690611f4e9060009086906004016155c8565b60606040518083038186803b158015611f6657600080fd5b505af4158015611f7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906148b6565b6040517fd09657e1000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d09657e190610c4690849086906004016155c8565b611ffb613ad6565b6040517f58105e35000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906358105e3590611102906000908690600401615550565b612056612546565b6120725760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661209b5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f0e7cd6f80000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90630e7cd6f890610df99060009085906004016155c8565b612103612546565b61211f5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166121485760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fa289756d0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063a289756d90610df9906000908590600401615527565b606080606080736b15c3f0d266be946ea2d33c40d1a7204c8403a263edf9c1ec6000876040518363ffffffff1660e01b81526004016121e8929190615550565b60006040518083038186803b15801561220057600080fd5b505af4158015612214573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261225a9190810190614712565b93509350935093509193509193565b612271612546565b61228d5760405162461bcd60e51b8152600401610d6790615302565b60135460405160009173ffffffffffffffffffffffffffffffffffffffff16907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3601380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6040517f5ebcae4c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290635ebcae4c9061234f9084906004016153bd565b60206040518083038186803b15801561236757600080fd5b505af415801561237b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c919081019061480e565b6123a7613ad6565b6040517f136a1dc7000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063136a1dc790611e4e906000906004016153bd565b805160005b818110156124ca57600083828151811061241357fe5b6020026020010151600001519050600084838151811061242f57fe5b60200260200101516020015190508061244957600261244c565b60015b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff881684529091529081902060ff9390931690925590517f4d7f317d2088d039c2a95a09fcbf9cc9191fad5905f883c937cc3d317c4a6327906124b89085908590615245565b60405180910390a250506001016123fd565b505050565b6124d7613ad6565b6040517f8828244f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638828244f906111029060009086906004016155c8565b60135473ffffffffffffffffffffffffffffffffffffffff1690565b60135460009073ffffffffffffffffffffffffffffffffffffffff1661256a6139fa565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6000612590612546565b6125ac5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166125d55760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fb25328e00000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063b25328e0906117d290600090879087906004016155e3565b6040517f92108b1e000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906392108b1e906113b690849086906004016153cb565b601354600160a01b900460ff166126b55760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fbd76ecfd00000000000000000000000000000000000000000000000000000000815273e4d3450d52edf515433fec12eaefffbfa83250b99063bd76ecfd90610f9890600090869086906004016154f3565b61271f613ad6565b6040517f2bdf1cf2000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290632bdf1cf2906111029060009086906004016155c8565b61277a612546565b6127965760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166127bf5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fdf7ff3630000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063df7ff36390610f989060009086908690600401615436565b612829612546565b6128455760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661286e5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f6f4814100000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90636f48141090610f98906000908690869060040161565c565b6128d8613b09565b6128e0613ae9565b6128e8613ad6565b6128f0613ad6565b6040517fba25f9d6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ba25f9d6906129439060009089906004016155c8565b6102a06040518083038186803b15801561295c57600080fd5b505af4158015612970573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061225a919081019061498e565b61299c613ad6565b6040517fa16184f6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a16184f6906111029060009086906004016155c8565b6129f7613ad6565b6129ff613ad6565b6040517fd933f844000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d933f84490612a52906000908790600401615550565b604080518083038186803b158015612a6957600080fd5b505af4158015612a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612aa19190810190614886565b9094909350915050565b612ab3612546565b612acf5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16612af85760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f54dd35160000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906354dd351690610df9906000908590600401615542565b6040517f4a451be2000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290634a451be2906116cc9084906004016153bd565b612bb3613ad6565b6040517f8339c50f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638339c50f90611e4e906000906004016153bd565b612c0c613abf565b6040517f634c50ce000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063634c50ce90612c61906000908790879060040161556b565b604080518083038186803b158015612c7857600080fd5b505af4158015612c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b469190810190614a41565b612cb8613abf565b6040517f055369d7000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063055369d790612d0b9060009086906004016155c8565b604080518083038186803b158015612d2257600080fd5b505af4158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a23565b612d62613ad6565b612d6a613abf565b5060408051808201825260008082526020820181905291517ffef3d3cb0000000000000000000000000000000000000000000000000000000081529091736b15c3f0d266be946ea2d33c40d1a7204c8403a29163fef3d3cb91612dd591859089908990600401615593565b60206040518083038186803b158015612ded57600080fd5b505af4158015612e01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e259190810190614868565b949350505050565b612e35613abf565b6040517fd2c9ee00000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d2c9ee0090610e9a9060009086906004016155c8565b612e90613abf565b6040517f485df94f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063485df94f90612d0b9060009086906004016155c8565b6040517f10b9711c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906310b9711c906116cc9084906004016153bd565b612f3e613ae9565b6040517f4aaee585000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290634aaee58590611f4e9060009086906004016155c8565b6040517f99c4c009000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906399c4c00990612fe89084908790879060040161556b565b60206040518083038186803b15801561300057600080fd5b505af4158015613014573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b469190810190614a7d565b6040517f4af71ebc000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290634af71ebc9061308d9084908690600401615550565b60206040518083038186803b1580156130a557600080fd5b505af41580156130b9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061482c565b6130e5613ad6565b6040517f041f2294000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063041f2294906111029060009086906004016155c8565b613140613ad6565b6040517f22ef2161000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906322ef216190611e4e906000906004016153bd565b613199612546565b6131b55760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166131de5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f5cb90a6a0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90635cb90a6a90610df9906000908590600401615527565b613246612546565b6132625760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661328b5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f692808450000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90636928084590610f989060009086908690600401615634565b6132f5612546565b6133115760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661333a5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f8c5dabd10000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90638c5dabd190610f989060009086908690600401615619565b6133a4613b8a565b6040517f8b19f9e8000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638b19f9e8906133f5906000906004016153bd565b60e06040518083038186803b15801561340d57600080fd5b505af4158015613421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614a05565b61344d613b09565b6040517f0b48557f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290630b48557f906134a09060009086906004016155c8565b6102006040518083038186803b1580156134b957600080fd5b505af41580156134cd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061496f565b6134f9612546565b6135155760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661353e5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f3c0c9ddc0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90633c0c9ddc90610f98906000908690869060040161540e565b6135a8613ad6565b6040517fdb32b3ca000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063db32b3ca906111029060009086906004016155c8565b613603612546565b61361f5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166136485760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fbc54053f0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063bc54053f906136b8906000908d908d908d908d908d908d908d908d908d9060040161545e565b60006040518083038186803b1580156136d057600080fd5b505af41580156136e4573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b1790555050505050505050505050565b61370e612546565b61372a5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166137535760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f10c87b060000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906310c87b0690610f9890600090869086906004016155fe565b6040517f3808f217000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290633808f21790610c4690849086906004016153cb565b6040517f10f1b88c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906310f1b88c9061234f9084906004016153bd565b613865612546565b6138815760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166138aa5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fe80f80240000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063e80f802490610f98906000908690869060040161540e565b613914612546565b6139305760405162461bcd60e51b8152600401610d6790615302565b613939816139fe565b50565b613944613ad6565b61394c613ad6565b6040517febec2e6c000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ebec2e6c90611264906000908790600401615550565b6139a7613ad6565b6040517fa335f47a000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a335f47a906111029060009086906004016155c8565b3390565b73ffffffffffffffffffffffffffffffffffffffff8116613a315760405162461bcd60e51b8152600401610d67906152f2565b60135460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3601380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b604080518082019091526000808252602082015290565b6040518060200160405280600081525090565b604080516060810182526000808252602082018190529181019190915290565b604080516101608101825260008082526020820152908101613b29613abf565b8152602001613b36613ae9565b81526000602082018190526040820152606001613b51613ad6565b8152602001613b5e613ad6565b8152602001613b6b613abf565b8152602001613b78613abf565b8152602001613b85613ad6565b905290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b8035611b4981615892565b8051611b4981615892565b600082601f830112613bed57600080fd5b8151613c00613bfb826156d5565b6156ae565b91508181835260208401935060208101905083856020840282011115613c2557600080fd5b60005b83811015613c515781613c3b8882613bd1565b8452506020928301929190910190600101613c28565b5050505092915050565b600082601f830112613c6c57600080fd5b8135613c7a613bfb826156d5565b81815260209384019390925082018360005b83811015613c515781358601613ca28882613f8c565b8452506020928301929190910190600101613c8c565b600082601f830112613cc957600080fd5b8135613cd7613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613cfc57600080fd5b60005b83811015613c515781613d12888261418f565b84525060209092019160409190910190600101613cff565b600082601f830112613d3b57600080fd5b8135613d49613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613d6e57600080fd5b60005b83811015613c515781613d8488826142dc565b84525060209092019160409190910190600101613d71565b600082601f830112613dad57600080fd5b8151613dbb613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613de057600080fd5b60005b83811015613c515781613df68882614317565b84525060209092019160409190910190600101613de3565b600082601f830112613e1f57600080fd5b8151613e2d613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613e5257600080fd5b60005b83811015613c515781613e688882614427565b84525060209092019160409190910190600101613e55565b600082601f830112613e9157600080fd5b8151613e9f613bfb826156d5565b91508181835260208401935060208101905083856020840282011115613ec457600080fd5b60005b83811015613c515781613eda8882614483565b8452506020928301929190910190600101613ec7565b8035611b49816158a6565b8051611b49816158a6565b600082601f830112613f1757600080fd5b8135613f25613bfb826156f6565b91508082526020830160208301858383011115613f4157600080fd5b613f4c838284615814565b50505092915050565b8035611b49816158af565b8051611b49816158af565b8035611b49816158b8565b8035611b49816158c5565b8051611b49816158d2565b60006101608284031215613f9f57600080fd5b613faa6101006156ae565b90506000613fb88484613f6b565b8252506020613fc984848301614478565b6020830152506040613fdd84828501614068565b60408301525060c0613ff184828501614478565b60608301525060e061400584828501614478565b60808301525061010061401a84828501613bc6565b60a08301525061012061402f84828501614478565b60c08301525061014082013567ffffffffffffffff81111561405057600080fd5b61405c84828501613f06565b60e08301525092915050565b60006080828403121561407a57600080fd5b61408460806156ae565b905060006140928484613ef0565b82525060206140a384848301613f76565b60208301525060406140b784828501613f76565b60408301525060606140cb84828501614478565b60608301525092915050565b6000602082840312156140e957600080fd5b6140f360206156ae565b905060006141018484614483565b82525092915050565b60006020828403121561411c57600080fd5b61412660206156ae565b905060006141018484614478565b60006060828403121561414657600080fd5b61415060606156ae565b9050600061415e8484614462565b825250602061416f84848301614462565b60208301525060406141838482850161448e565b60408301525092915050565b6000604082840312156141a157600080fd5b6141ab60406156ae565b905060006141b98484613bc6565b82525060206141ca84848301614478565b60208301525092915050565b600061020082840312156141e957600080fd5b6141f46101606156ae565b905060006142028484613bd1565b825250602061421384848301613efb565b6020830152506040614227848285016143fd565b604083015250608061423b84828501614134565b60608301525060e061424f84828501613f60565b60808301525061010061426484828501613f60565b60a083015250610120614279848285016140d7565b60c08301525061014061428e848285016140d7565b60e0830152506101606142a384828501614427565b610100830152506101a06142b984828501614427565b610120830152506101e06142cf848285016140d7565b6101408301525092915050565b6000604082840312156142ee57600080fd5b6142f860406156ae565b905060006143068484613bc6565b82525060206141ca84848301613ef0565b60006040828403121561432957600080fd5b61433360406156ae565b905060006143418484613efb565b82525060206141ca8484830161446d565b600060e0828403121561436457600080fd5b61436e60e06156ae565b9050600061437c8484614499565b825250602061438d84848301614499565b60208301525060406143a184828501614499565b60408301525060606143b584828501614499565b60608301525060806143c984828501614499565b60808301525060a06143dd848285016144a4565b60a08301525060c06143f18482850161446d565b60c08301525092915050565b60006040828403121561440f57600080fd5b61441960406156ae565b90506000614341848461446d565b60006040828403121561443957600080fd5b61444360406156ae565b905060006144518484613efb565b82525060206141ca84848301614483565b8051611b49816158df565b8051611b49816158e8565b8035611b49816158f1565b8051611b49816158f1565b8051611b49816158fa565b8051611b4981615903565b8051611b498161590c565b6000602082840312156144c157600080fd5b6000612e258484613bc6565b6000602082840312156144df57600080fd5b6000612e258484613bd1565b600080604083850312156144fe57600080fd5b600061450a8585613bc6565b925050602061451b85828601613bc6565b9150509250929050565b6000806040838503121561453857600080fd5b60006145448585613bc6565b925050602061451b85828601613ef0565b6000806040838503121561456857600080fd5b60006145748585613bc6565b925050602061451b85828601613f55565b60008060008060008060008060006101208a8c0312156145a457600080fd5b60006145b08c8c613bc6565b99505060206145c18c828d01613f55565b98505060406145d28c828d01613f55565b97505060606145e38c828d0161410a565b96505060806145f48c828d0161410a565b95505060a06146058c828d01614478565b94505060c06146168c828d01614478565b93505060e06146278c828d0161410a565b9250506101006146398c828d01613ef0565b9150509295985092959850929598565b6000806040838503121561465c57600080fd5b823567ffffffffffffffff81111561467357600080fd5b61467f85828601613cb8565b925050602083013567ffffffffffffffff81111561469c57600080fd5b61451b85828601613c5b565b6000602082840312156146ba57600080fd5b813567ffffffffffffffff8111156146d157600080fd5b612e2584828501613d2a565b6000602082840312156146ef57600080fd5b815167ffffffffffffffff81111561470657600080fd5b612e2584828501613e80565b6000806000806080858703121561472857600080fd5b845167ffffffffffffffff81111561473f57600080fd5b61474b87828801613e80565b945050602085015167ffffffffffffffff81111561476857600080fd5b61477487828801613bdc565b935050604085015167ffffffffffffffff81111561479157600080fd5b61479d87828801613d9c565b925050606085015167ffffffffffffffff8111156147ba57600080fd5b6147c687828801613e0e565b91505092959194509250565b6000602082840312156147e457600080fd5b6000612e258484613efb565b60006020828403121561480257600080fd5b6000612e258484613f55565b60006020828403121561482057600080fd5b6000612e258484613f60565b60006020828403121561483e57600080fd5b6000612e258484613f81565b60006020828403121561485c57600080fd5b6000612e25848461410a565b60006020828403121561487a57600080fd5b6000612e2584846140d7565b6000806040838503121561489957600080fd5b60006148a585856140d7565b925050602061451b858286016140d7565b6000606082840312156148c857600080fd5b6000612e258484614134565b6000604082840312156148e657600080fd5b6000612e25848461418f565b6000806060838503121561490557600080fd5b6000614911858561418f565b925050604061451b85828601614478565b60008060006080848603121561493757600080fd5b6000614943868661418f565b935050604061495486828701614478565b925050606061496586828701614478565b9150509250925092565b6000610200828403121561498257600080fd5b6000612e2584846141d6565b6000806000806102a085870312156149a557600080fd5b60006149b187876141d6565b9450506102006149c387828801614134565b9350506102606149d5878288016140d7565b9250506102806147c6878288016140d7565b6000604082840312156149f957600080fd5b6000612e258484614317565b600060e08284031215614a1757600080fd5b6000612e258484614352565b600060408284031215614a3557600080fd5b6000612e2584846143fd565b600060408284031215614a5357600080fd5b6000612e258484614427565b600060208284031215614a7157600080fd5b6000612e258484614478565b600060208284031215614a8f57600080fd5b6000612e258484614483565b60008060408385031215614aae57600080fd5b600061450a8585614478565b60008060408385031215614acd57600080fd5b60006145448585614478565b60008060408385031215614aec57600080fd5b60006145748585614478565b60008060408385031215614b0b57600080fd5b6000614b178585614478565b925050602061451b8582860161410a565b60008060408385031215614b3b57600080fd5b6000614b478585614478565b925050602061451b85828601614478565b6000614b648383614bb0565b505060200190565b6000611b468383614ef4565b6000614b848383615037565b505060400190565b6000614b848383615134565b6000614b8483836151f0565b6000614b648383615213565b614bb98161574f565b82525050565b6000614bca82615742565b614bd48185615746565b9350614bdf8361573c565b8060005b83811015614c0d578151614bf78882614b58565b9750614c028361573c565b925050600101614be3565b509495945050505050565b6000614c2382615742565b614c2d8185615746565b935083602082028501614c3f8561573c565b8060005b85811015614c795784840389528151614c5c8582614b6c565b9450614c678361573c565b60209a909a0199925050600101614c43565b5091979650505050505050565b6000614c9182615742565b614c9b8185615746565b9350614ca68361573c565b8060005b83811015614c0d578151614cbe8882614b78565b9750614cc98361573c565b925050600101614caa565b6000614cdf82615742565b614ce98185615746565b9350614cf48361573c565b8060005b83811015614c0d578151614d0c8882614b8c565b9750614d178361573c565b925050600101614cf8565b6000614d2d82615742565b614d378185615746565b9350614d428361573c565b8060005b83811015614c0d578151614d5a8882614b98565b9750614d658361573c565b925050600101614d46565b6000614d7b82615742565b614d858185615746565b9350614d908361573c565b8060005b83811015614c0d578151614da88882614ba4565b9750614db38361573c565b925050600101614d94565b614bb98161575a565b6000614dd282615742565b614ddc8185615746565b9350614dec818560208601615820565b614df58161584c565b9093019392505050565b614bb98161575f565b614bb9816157f3565b614bb9816157fe565b614bb981615809565b6000614e30602683615746565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000614e8f602083615746565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000614ec8601f83615746565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b8051600090610160840190614f098582614e08565b506020830151614f1c6020860182615213565b506040830151614f2f6040860182614f9f565b506060830151614f4260c0860182615213565b506080830151614f5560e0860182615213565b5060a0830151614f69610100860182614bb0565b5060c0830151614f7d610120860182615213565b5060e0830151848203610140860152614f968282614dc7565b95945050505050565b80516080830190614fb08482614dbe565b506020820151614fc36020850182614e11565b506040820151614fd66040850182614e11565b506060820151614fe96060850182615213565b50505050565b80516020830190614fe98482615213565b805160608301906150118482615201565b5060208201516150246020850182615201565b506040820151614fe9604085018261521c565b805160408301906150488482614bb0565b506020820151614fe96020850182615213565b805161020083019061506d8482614bb0565b5060208201516150806020850182614dbe565b50604082015161509360408501826151df565b5060608201516150a66080850182615000565b5060808201516150b960e0850182614dff565b5060a08201516150cd610100850182614dff565b5060c08201516150e1610120850182614fef565b5060e08201516150f5610140850182614fef565b5061010082015161510a6101608501826151f0565b5061012082015161511f6101a08501826151f0565b50610140820151614fe96101e0850182614fef565b805160408301906151458482614dbe565b506020820151614fe9602085018261520a565b805160e08301906151698482615225565b50602082015161517c6020850182615225565b50604082015161518f6040850182615225565b5060608201516151a26060850182615225565b5060808201516151b56080850182615225565b5060a08201516151c860a085018261522e565b5060c0820151614fe960c085018261520a565b9052565b80516040830190615145848261520a565b805160408301906150488482614dbe565b614bb981615788565b614bb98161579b565b614bb9816157c9565b614bb9816157cc565b614bb9816157d5565b614bb9816157e2565b60208101611b498284614bb0565b604081016152538285614bb0565b6116176020830184614dbe565b60208082528101611b468184614d70565b608080825281016152828187614d70565b905081810360208301526152968186614bbf565b905081810360408301526152aa8185614cd4565b905081810360608301526152be8184614d22565b9695505050505050565b60208101611b498284614dbe565b60208101611b498284614dff565b60208101611b498284614e1a565b60208082528101610c9681614e23565b60208082528101610c9681614e82565b60208082528101610c9681614ebb565b60208101611b498284614fef565b6040810161533e8285614fef565b6116176020830184614fef565b60608101611b498284615000565b6102008101611b49828461505b565b6102a08101615377828761505b565b615385610200830186615000565b615393610260830185614fef565b614f96610280830184614fef565b60408101611b498284615134565b60e08101611b498284615158565b60208101611b4982846151db565b604081016153d982856151db565b6116176020830184614bb0565b606081016153f482866151db565b6154016020830185614bb0565b612e256040830184614bb0565b6060810161541c82866151db565b6154296020830185614bb0565b612e256040830184614dbe565b6060810161544482866151db565b6154516020830185614bb0565b612e256040830184614dff565b610140810161546d828d6151db565b61547a602083018c614bb0565b615487604083018b614dff565b615494606083018a614dff565b6154a16080830189614fef565b6154ae60a0830188614fef565b6154bb60c0830187615213565b6154c860e0830186615213565b6154d6610100830185614fef565b6154e4610120830184614dbe565b9b9a5050505050505050505050565b6060810161550182866151db565b81810360208301526155138185614c86565b90508181036040830152614f968184614c18565b6040810161553582856151db565b6116176020830184614dff565b6040810161533e82856151db565b6060810161555e82856151db565b6116176020830184615037565b6080810161557982866151db565b6155866020830185615037565b612e256060830184615213565b60a081016155a182876151db565b6155ae6020830186615037565b6155bb6060830185615213565b614f966080830184615213565b604081016155d682856151db565b6116176020830184615213565b606081016155f182866151db565b6154016020830185615213565b6060810161560c82866151db565b6154296020830185615213565b6060810161562782866151db565b6154516020830185615213565b6060810161564282866151db565b61564f6020830185615213565b612e256040830184614fef565b6060810161566a82866151db565b6156776020830185615213565b612e256040830184615213565b60408101611b4982846151df565b60408101611b4982846151f0565b60208101611b498284615213565b60405181810167ffffffffffffffff811182821017156156cd57600080fd5b604052919050565b600067ffffffffffffffff8211156156ec57600080fd5b5060209081020190565b600067ffffffffffffffff82111561570d57600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b90815260200190565b6000610c96826157b0565b151590565b6000610c968261574f565b80610c9981615874565b80610c998161587e565b80610c9981615888565b6dffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b63ffffffff1690565b67ffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b6000610c968261576a565b6000610c9682615774565b6000610c968261577e565b82818337506000910152565b60005b8381101561583b578181015183820152602001615823565b83811115614fe95750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690565b6009811061393957fe5b6002811061393957fe5b6003811061393957fe5b61589b8161574f565b811461393957600080fd5b61589b8161575a565b61589b8161575f565b6009811061393957600080fd5b6002811061393957600080fd5b6003811061393957600080fd5b61589b81615788565b61589b8161579b565b61589b816157c9565b61589b816157cc565b61589b816157d5565b61589b816157e256fea365627a7a72315820c64395773a7b5a30ad260a8ffa24ee15bf3b0f82191b0bf84f576e35341ba3136c6578706572696d656e74616cf564736f6c634300051000400000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000001bc16d674ec800000000000000000000000000000000000000000000000000004563918244f400000000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000004b3b4ca85a86c47a098a2240000000000000000000000000000000000000000000000000000000000214e8348c4f000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000bcbce7f1b150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000009f7779a8af28de91262ba9d6383fef899102e87600000000000000000000000000000000000000000000000000000000001e8480

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106105ac5760003560e01c80638da5cb5b116102ed578063deec053d11610191578063ec6e3987116100ee578063f2385be3116100a2578063f94160521161007c578063f941605214610bcb578063fd04b60614610bde578063fd47eda614610956576105ac565b8063f2385be314610b9d578063f2901ae214610ba5578063f2fde38b14610bb8576105ac565b8063ed4d6f5c116100d3578063ed4d6f5c14610b64578063ef6957d014610b77578063f1061ba814610b8a576105ac565b8063ec6e398714610b3e578063ecef481f14610b51576105ac565b8063e56659fc11610145578063e8e72f751161012a578063e8e72f7514610af6578063eb1c6e6b14610b09578063eb44fdd314610b1e576105ac565b8063e56659fc14610ad0578063e5e23ef214610ae3576105ac565b8063e51bfcb411610176578063e51bfcb414610a95578063e52828c714610ab5578063e552022814610ac8576105ac565b8063deec053d14610a6f578063e0e48d1914610a82576105ac565b8063ba77ea671161024a578063cb04a34c116101fe578063d93c9591116101d8578063d93c959114610796578063dc6d784614610a54578063de8be8e714610a67576105ac565b8063cb04a34c14610a0e578063d24c48bc14610a2e578063d5ecf7c514610a41576105ac565b8063c0e044fc1161022f578063c0e044fc146109eb578063c1460942146109f3578063c190c2ec146109fb576105ac565b8063ba77ea67146109c5578063c0bb72b7146109d8576105ac565b8063ac7bb20b116102a1578063b306e4f811610286578063b306e4f81461097c578063b548b8921461098f578063b64e31ea146109b2576105ac565b8063ac7bb20b14610956578063b0dc49f814610969576105ac565b80638f6bc659116102d25780638f6bc6591461091d5780638fae3be114610930578063a67a6a4514610943576105ac565b80638da5cb5b1461090d5780638f32d59b14610915576105ac565b8063384027621161045457806356ea84b2116103b15780636a8194e7116103655780637e9eaf411161033f5780637e9eaf41146108df57806385b53fc8146108e75780638928378e146108fa576105ac565b80636a8194e7146108ac578063715018a6146108cf578063770e43e0146108d7576105ac565b80635d3c0ce8116103965780635d3c0ce81461087357806363ae56b31461088657806366411ff414610899576105ac565b806356ea84b2146108405780635ac7d17c14610860576105ac565b80634be87414116104085780634e634067116103ed5780634e6340671461081d5780634f3c1542146108255780634f4d66f61461082d576105ac565b80634be87414146108025780634db90d5214610815576105ac565b80633a031bf0116104395780633a031bf0146107bc57806347d1b53c146107cf57806348e648ee146107ef576105ac565b80633840276214610796578063387a498a146107a9576105ac565b806317b505251161050d57806320945e26116104c15780632e822af31161049b5780632e822af3146107705780633063bce2146107835780633305e5bb14610709576105ac565b806320945e2614610742578063295c39a5146107555780632a5608451461075d576105ac565b80631a7777bb116104f25780631a7777bb146107095780631ae4ec521461071c5780631eb120761461072f576105ac565b806317b50525146106d6578063197f0f05146106f6576105ac565b80630f47fab011610564578063121fb72f11610549578063121fb72f14610682578063124f914c1461069557806313368364146106b6576105ac565b80630f47fab014610642578063105de10c14610662576105ac565b80630781d820116105955780630781d820146105fa57806307af15681461060f5780630b28fa3e1461062f576105ac565b8063052f72d7146105b1578063062bd3e9146105da575b600080fd5b6105c46105bf3660046144af565b610bf1565b6040516105d191906152c8565b60405180910390f35b6105ed6105e8366004614a5f565b610c9e565b6040516105d19190615237565b61060d610608366004614a5f565b610d43565b005b61062261061d366004614a5f565b610e3f565b6040516105d19190615692565b61060d61063d366004614af8565b610ee9565b6106556106503660046148d4565b610fdf565b6040516105d19190615260565b6106756106703660046148d4565b6110a7565b6040516105d19190615322565b61060d610690366004614ad9565b611152565b6106a86106a33660046148d4565b611201565b6040516105d1929190615330565b6106c96106c4366004614a5f565b6112bc565b6040516105d191906152d6565b6106e96106e43660046148d4565b611361565b6040516105d191906156a0565b6106c9610704366004614a5f565b611406565b610675610717366004614a5f565b61145b565b61060d61072a366004614b28565b6114b6565b61067561073d366004614922565b611565565b6106756107503660046148d4565b61161e565b6106e9611679565b6106e961076b3660046144eb565b611721565b61060d61077e36600461484a565b61183d565b61060d61079136600461484a565b6118ea565b6106226107a4366004614a5f565b611997565b61060d6107b736600461484a565b6119f2565b6105c46107ca3660046144eb565b611a9f565b6107e26107dd3660046148f2565b611b4f565b6040516105d191906153a1565b6106c96107fd3660046144af565b611bfb565b61060d610810366004614af8565b611c50565b6105c4611cff565b6105c4611da2565b610675611df5565b6106e961083b3660046148d4565b611e9e565b61085361084e366004614a5f565b611ef3565b6040516105d1919061534b565b6105c461086e366004614a5f565b611f9e565b6106756108813660046148d4565b611ff3565b61060d610894366004614a5f565b61204e565b61060d6108a73660046147f0565b6120fb565b6108bf6108ba3660046148d4565b6121a8565b6040516105d19493929190615271565b61060d612269565b6106c96122fc565b61067561239f565b61060d6108f53660046146a8565b6123f8565b610675610908366004614a5f565b6124cf565b6105ed61252a565b6105c4612546565b6106e961092b366004614a9b565b612586565b6106e961093e3660046144af565b612637565b61060d610951366004614649565b61268c565b610675610964366004614a5f565b612717565b61060d610977366004614555565b612772565b61060d61098a366004614b28565b612821565b6109a261099d366004614a5f565b6128d0565b6040516105d19493929190615368565b6106756109c0366004614a5f565b612994565b6106a86109d33660046148d4565b6129ef565b61060d6109e636600461484a565b612aab565b6106e9612b58565b610675612bab565b610622610a093660046148f2565b612c04565b610a21610a1c366004614a5f565b612cb0565b6040516105d19190615684565b610675610a3c366004614b28565b612d5a565b610622610a4f366004614a5f565b612e2d565b610a21610a62366004614a5f565b612e88565b6106e9612ee3565b610853610a7d366004614a5f565b612f36565b6106e9610a903660046148f2565b612f91565b610aa8610aa33660046148d4565b613038565b6040516105d191906152e4565b610675610ac3366004614a5f565b6130dd565b610675613138565b61060d610ade3660046147f0565b613191565b61060d610af1366004614af8565b61323e565b61060d610b04366004614ad9565b6132ed565b610b1161339c565b6040516105d191906153af565b610b31610b2c366004614a5f565b613445565b6040516105d19190615359565b61060d610b4c366004614525565b6134f1565b610675610b5f366004614a5f565b6135a0565b61060d610b72366004614585565b6135fb565b61060d610b85366004614aba565b613706565b6105c4610b983660046144af565b6137b5565b6106c961380a565b61060d610bb3366004614525565b61385d565b61060d610bc63660046144af565b61390c565b6106a8610bd93660046148d4565b61393c565b610675610bec366004614a5f565b61399f565b6040517f288afc62000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063288afc6290610c4690849086906004016153cb565b60206040518083038186803b158015610c5e57600080fd5b505af4158015610c72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906147d2565b90505b919050565b6040517fa2169a3a000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a2169a3a90610cf390849086906004016155c8565b60206040518083038186803b158015610d0b57600080fd5b505af4158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906144cd565b610d4b612546565b610d705760405162461bcd60e51b8152600401610d6790615302565b60405180910390fd5b601354600160a01b900460ff16610d995760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f70b6151c0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906370b6151c90610df99060009085906004016155c8565b60006040518083038186803b158015610e1157600080fd5b505af4158015610e25573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b179055505050565b610e47613abf565b6040517f7462482a000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290637462482a90610e9a9060009086906004016155c8565b604080518083038186803b158015610eb157600080fd5b505af4158015610ec5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a41565b610ef1612546565b610f0d5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16610f365760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f1c90d8240000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90631c90d82490610f989060009086908690600401615634565b60006040518083038186803b158015610fb057600080fd5b505af4158015610fc4573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b17905550505050565b6040517fbbf70398000000000000000000000000000000000000000000000000000000008152606090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063bbf7039890611035906000908690600401615550565b60006040518083038186803b15801561104d57600080fd5b505af4158015611061573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c9691908101906146dd565b6110af613ad6565b6040517fc61f66b4000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063c61f66b490611102906000908690600401615550565b60206040518083038186803b15801561111a57600080fd5b505af415801561112e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614868565b61115a612546565b6111765760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661119f5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517faa92e3480000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063aa92e34890610f989060009086908690600401615619565b611209613ad6565b611211613ad6565b6040517fddc7c6f0000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ddc7c6f090611264906000908790600401615550565b604080518083038186803b15801561127b57600080fd5b505af415801561128f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112b39190810190614886565b91509150915091565b6040517fa18862fc000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a18862fc9061131190849086906004016155c8565b60206040518083038186803b15801561132957600080fd5b505af415801561133d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061480e565b6040517f6a0e6433000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290636a0e6433906113b69084908690600401615550565b60206040518083038186803b1580156113ce57600080fd5b505af41580156113e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a7d565b6040517f35b5d1f6000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906335b5d1f69061131190849086906004016155c8565b611463613ad6565b6040517f063bf373000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063063bf373906111029060009086906004016155c8565b6114be612546565b6114da5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166115035760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f3ea418830000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90633ea4188390610f98906000908690869060040161565c565b61156d613ad6565b6040517ffef3d3cb000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063fef3d3cb906115c490600090889088908890600401615593565b60206040518083038186803b1580156115dc57600080fd5b505af41580156115f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116149190810190614868565b90505b9392505050565b611626613ad6565b6040517f62f8e1b3000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906362f8e1b390611102906000908690600401615550565b6040517fe99388d3000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063e99388d3906116cc9084906004016153bd565b60206040518083038186803b1580156116e457600080fd5b505af41580156116f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614a7d565b905090565b600061172b612546565b6117475760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166117705760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fdd32998a0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063dd32998a906117d290600090879087906004016153e6565b60206040518083038186803b1580156117ea57600080fd5b505af41580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118229190810190614a7d565b90506013805460ff60a01b1916600160a01b17905592915050565b611845612546565b6118615760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661188a5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517ff9cacc4c0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063f9cacc4c90610df9906000908590600401615542565b6118f2612546565b61190e5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166119375760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f0f44e6bc0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90630f44e6bc90610df9906000908590600401615542565b61199f613abf565b6040517fc641ccd1000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063c641ccd190610e9a9060009086906004016155c8565b6119fa612546565b611a165760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16611a3f5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f311401d00000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063311401d090610df9906000908590600401615542565b6040517f75bcaa48000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906375bcaa4890611af6908490879087906004016153e6565b60206040518083038186803b158015611b0e57600080fd5b505af4158015611b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4691908101906147d2565b90505b92915050565b611b57613abf565b6040517f787f7727000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063787f772790611bac906000908790879060040161556b565b604080518083038186803b158015611bc357600080fd5b505af4158015611bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4691908101906149e7565b6040517f2e19a6ea000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290632e19a6ea9061131190849086906004016153cb565b611c58612546565b611c745760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16611c9d5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f7335079b0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90637335079b90610f989060009086908690600401615634565b6040517f83fe9e14000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906383fe9e1490611d529084906004016153bd565b60206040518083038186803b158015611d6a57600080fd5b505af4158015611d7e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c91908101906147d2565b6040517f6861138d000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290636861138d90611d529084906004016153bd565b611dfd613ad6565b6040517f4f820bd6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290634f820bd690611e4e906000906004016153bd565b60206040518083038186803b158015611e6657600080fd5b505af4158015611e7a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614868565b6040517fe647a687000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063e647a687906113b69084908690600401615550565b611efb613ae9565b6040517fd23406b6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d23406b690611f4e9060009086906004016155c8565b60606040518083038186803b158015611f6657600080fd5b505af4158015611f7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c9691908101906148b6565b6040517fd09657e1000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d09657e190610c4690849086906004016155c8565b611ffb613ad6565b6040517f58105e35000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906358105e3590611102906000908690600401615550565b612056612546565b6120725760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661209b5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f0e7cd6f80000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90630e7cd6f890610df99060009085906004016155c8565b612103612546565b61211f5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166121485760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fa289756d0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063a289756d90610df9906000908590600401615527565b606080606080736b15c3f0d266be946ea2d33c40d1a7204c8403a263edf9c1ec6000876040518363ffffffff1660e01b81526004016121e8929190615550565b60006040518083038186803b15801561220057600080fd5b505af4158015612214573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261225a9190810190614712565b93509350935093509193509193565b612271612546565b61228d5760405162461bcd60e51b8152600401610d6790615302565b60135460405160009173ffffffffffffffffffffffffffffffffffffffff16907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3601380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6040517f5ebcae4c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290635ebcae4c9061234f9084906004016153bd565b60206040518083038186803b15801561236757600080fd5b505af415801561237b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c919081019061480e565b6123a7613ad6565b6040517f136a1dc7000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063136a1dc790611e4e906000906004016153bd565b805160005b818110156124ca57600083828151811061241357fe5b6020026020010151600001519050600084838151811061242f57fe5b60200260200101516020015190508061244957600261244c565b60015b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff881684529091529081902060ff9390931690925590517f4d7f317d2088d039c2a95a09fcbf9cc9191fad5905f883c937cc3d317c4a6327906124b89085908590615245565b60405180910390a250506001016123fd565b505050565b6124d7613ad6565b6040517f8828244f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638828244f906111029060009086906004016155c8565b60135473ffffffffffffffffffffffffffffffffffffffff1690565b60135460009073ffffffffffffffffffffffffffffffffffffffff1661256a6139fa565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6000612590612546565b6125ac5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166125d55760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fb25328e00000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063b25328e0906117d290600090879087906004016155e3565b6040517f92108b1e000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906392108b1e906113b690849086906004016153cb565b601354600160a01b900460ff166126b55760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fbd76ecfd00000000000000000000000000000000000000000000000000000000815273e4d3450d52edf515433fec12eaefffbfa83250b99063bd76ecfd90610f9890600090869086906004016154f3565b61271f613ad6565b6040517f2bdf1cf2000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290632bdf1cf2906111029060009086906004016155c8565b61277a612546565b6127965760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166127bf5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fdf7ff3630000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063df7ff36390610f989060009086908690600401615436565b612829612546565b6128455760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661286e5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f6f4814100000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90636f48141090610f98906000908690869060040161565c565b6128d8613b09565b6128e0613ae9565b6128e8613ad6565b6128f0613ad6565b6040517fba25f9d6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ba25f9d6906129439060009089906004016155c8565b6102a06040518083038186803b15801561295c57600080fd5b505af4158015612970573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061225a919081019061498e565b61299c613ad6565b6040517fa16184f6000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a16184f6906111029060009086906004016155c8565b6129f7613ad6565b6129ff613ad6565b6040517fd933f844000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d933f84490612a52906000908790600401615550565b604080518083038186803b158015612a6957600080fd5b505af4158015612a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612aa19190810190614886565b9094909350915050565b612ab3612546565b612acf5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff16612af85760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f54dd35160000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906354dd351690610df9906000908590600401615542565b6040517f4a451be2000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290634a451be2906116cc9084906004016153bd565b612bb3613ad6565b6040517f8339c50f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638339c50f90611e4e906000906004016153bd565b612c0c613abf565b6040517f634c50ce000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063634c50ce90612c61906000908790879060040161556b565b604080518083038186803b158015612c7857600080fd5b505af4158015612c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b469190810190614a41565b612cb8613abf565b6040517f055369d7000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063055369d790612d0b9060009086906004016155c8565b604080518083038186803b158015612d2257600080fd5b505af4158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c969190810190614a23565b612d62613ad6565b612d6a613abf565b5060408051808201825260008082526020820181905291517ffef3d3cb0000000000000000000000000000000000000000000000000000000081529091736b15c3f0d266be946ea2d33c40d1a7204c8403a29163fef3d3cb91612dd591859089908990600401615593565b60206040518083038186803b158015612ded57600080fd5b505af4158015612e01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e259190810190614868565b949350505050565b612e35613abf565b6040517fd2c9ee00000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063d2c9ee0090610e9a9060009086906004016155c8565b612e90613abf565b6040517f485df94f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063485df94f90612d0b9060009086906004016155c8565b6040517f10b9711c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906310b9711c906116cc9084906004016153bd565b612f3e613ae9565b6040517f4aaee585000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290634aaee58590611f4e9060009086906004016155c8565b6040517f99c4c009000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906399c4c00990612fe89084908790879060040161556b565b60206040518083038186803b15801561300057600080fd5b505af4158015613014573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b469190810190614a7d565b6040517f4af71ebc000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290634af71ebc9061308d9084908690600401615550565b60206040518083038186803b1580156130a557600080fd5b505af41580156130b9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061482c565b6130e5613ad6565b6040517f041f2294000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063041f2294906111029060009086906004016155c8565b613140613ad6565b6040517f22ef2161000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a2906322ef216190611e4e906000906004016153bd565b613199612546565b6131b55760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166131de5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f5cb90a6a0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90635cb90a6a90610df9906000908590600401615527565b613246612546565b6132625760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661328b5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f692808450000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90636928084590610f989060009086908690600401615634565b6132f5612546565b6133115760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661333a5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f8c5dabd10000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90638c5dabd190610f989060009086908690600401615619565b6133a4613b8a565b6040517f8b19f9e8000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290638b19f9e8906133f5906000906004016153bd565b60e06040518083038186803b15801561340d57600080fd5b505af4158015613421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171c9190810190614a05565b61344d613b09565b6040517f0b48557f000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a290630b48557f906134a09060009086906004016155c8565b6102006040518083038186803b1580156134b957600080fd5b505af41580156134cd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c96919081019061496f565b6134f9612546565b6135155760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff1661353e5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f3c0c9ddc0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae90633c0c9ddc90610f98906000908690869060040161540e565b6135a8613ad6565b6040517fdb32b3ca000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063db32b3ca906111029060009086906004016155c8565b613603612546565b61361f5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166136485760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fbc54053f0000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063bc54053f906136b8906000908d908d908d908d908d908d908d908d908d9060040161545e565b60006040518083038186803b1580156136d057600080fd5b505af41580156136e4573d6000803e3d6000fd5b50506013805460ff60a01b1916600160a01b1790555050505050505050505050565b61370e612546565b61372a5760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166137535760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517f10c87b060000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae906310c87b0690610f9890600090869086906004016155fe565b6040517f3808f217000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a290633808f21790610c4690849086906004016153cb565b6040517f10f1b88c000000000000000000000000000000000000000000000000000000008152600090736b15c3f0d266be946ea2d33c40d1a7204c8403a2906310f1b88c9061234f9084906004016153bd565b613865612546565b6138815760405162461bcd60e51b8152600401610d6790615302565b601354600160a01b900460ff166138aa5760405162461bcd60e51b8152600401610d6790615312565b6013805460ff60a01b191690556040517fe80f80240000000000000000000000000000000000000000000000000000000081527343c2fdb89a1c491f9fe86e1ff05bd2be204ab4ae9063e80f802490610f98906000908690869060040161540e565b613914612546565b6139305760405162461bcd60e51b8152600401610d6790615302565b613939816139fe565b50565b613944613ad6565b61394c613ad6565b6040517febec2e6c000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063ebec2e6c90611264906000908790600401615550565b6139a7613ad6565b6040517fa335f47a000000000000000000000000000000000000000000000000000000008152736b15c3f0d266be946ea2d33c40d1a7204c8403a29063a335f47a906111029060009086906004016155c8565b3390565b73ffffffffffffffffffffffffffffffffffffffff8116613a315760405162461bcd60e51b8152600401610d67906152f2565b60135460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3601380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b604080518082019091526000808252602082015290565b6040518060200160405280600081525090565b604080516060810182526000808252602082018190529181019190915290565b604080516101608101825260008082526020820152908101613b29613abf565b8152602001613b36613ae9565b81526000602082018190526040820152606001613b51613ad6565b8152602001613b5e613ad6565b8152602001613b6b613abf565b8152602001613b78613abf565b8152602001613b85613ad6565b905290565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b8035611b4981615892565b8051611b4981615892565b600082601f830112613bed57600080fd5b8151613c00613bfb826156d5565b6156ae565b91508181835260208401935060208101905083856020840282011115613c2557600080fd5b60005b83811015613c515781613c3b8882613bd1565b8452506020928301929190910190600101613c28565b5050505092915050565b600082601f830112613c6c57600080fd5b8135613c7a613bfb826156d5565b81815260209384019390925082018360005b83811015613c515781358601613ca28882613f8c565b8452506020928301929190910190600101613c8c565b600082601f830112613cc957600080fd5b8135613cd7613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613cfc57600080fd5b60005b83811015613c515781613d12888261418f565b84525060209092019160409190910190600101613cff565b600082601f830112613d3b57600080fd5b8135613d49613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613d6e57600080fd5b60005b83811015613c515781613d8488826142dc565b84525060209092019160409190910190600101613d71565b600082601f830112613dad57600080fd5b8151613dbb613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613de057600080fd5b60005b83811015613c515781613df68882614317565b84525060209092019160409190910190600101613de3565b600082601f830112613e1f57600080fd5b8151613e2d613bfb826156d5565b91508181835260208401935060208101905083856040840282011115613e5257600080fd5b60005b83811015613c515781613e688882614427565b84525060209092019160409190910190600101613e55565b600082601f830112613e9157600080fd5b8151613e9f613bfb826156d5565b91508181835260208401935060208101905083856020840282011115613ec457600080fd5b60005b83811015613c515781613eda8882614483565b8452506020928301929190910190600101613ec7565b8035611b49816158a6565b8051611b49816158a6565b600082601f830112613f1757600080fd5b8135613f25613bfb826156f6565b91508082526020830160208301858383011115613f4157600080fd5b613f4c838284615814565b50505092915050565b8035611b49816158af565b8051611b49816158af565b8035611b49816158b8565b8035611b49816158c5565b8051611b49816158d2565b60006101608284031215613f9f57600080fd5b613faa6101006156ae565b90506000613fb88484613f6b565b8252506020613fc984848301614478565b6020830152506040613fdd84828501614068565b60408301525060c0613ff184828501614478565b60608301525060e061400584828501614478565b60808301525061010061401a84828501613bc6565b60a08301525061012061402f84828501614478565b60c08301525061014082013567ffffffffffffffff81111561405057600080fd5b61405c84828501613f06565b60e08301525092915050565b60006080828403121561407a57600080fd5b61408460806156ae565b905060006140928484613ef0565b82525060206140a384848301613f76565b60208301525060406140b784828501613f76565b60408301525060606140cb84828501614478565b60608301525092915050565b6000602082840312156140e957600080fd5b6140f360206156ae565b905060006141018484614483565b82525092915050565b60006020828403121561411c57600080fd5b61412660206156ae565b905060006141018484614478565b60006060828403121561414657600080fd5b61415060606156ae565b9050600061415e8484614462565b825250602061416f84848301614462565b60208301525060406141838482850161448e565b60408301525092915050565b6000604082840312156141a157600080fd5b6141ab60406156ae565b905060006141b98484613bc6565b82525060206141ca84848301614478565b60208301525092915050565b600061020082840312156141e957600080fd5b6141f46101606156ae565b905060006142028484613bd1565b825250602061421384848301613efb565b6020830152506040614227848285016143fd565b604083015250608061423b84828501614134565b60608301525060e061424f84828501613f60565b60808301525061010061426484828501613f60565b60a083015250610120614279848285016140d7565b60c08301525061014061428e848285016140d7565b60e0830152506101606142a384828501614427565b610100830152506101a06142b984828501614427565b610120830152506101e06142cf848285016140d7565b6101408301525092915050565b6000604082840312156142ee57600080fd5b6142f860406156ae565b905060006143068484613bc6565b82525060206141ca84848301613ef0565b60006040828403121561432957600080fd5b61433360406156ae565b905060006143418484613efb565b82525060206141ca8484830161446d565b600060e0828403121561436457600080fd5b61436e60e06156ae565b9050600061437c8484614499565b825250602061438d84848301614499565b60208301525060406143a184828501614499565b60408301525060606143b584828501614499565b60608301525060806143c984828501614499565b60808301525060a06143dd848285016144a4565b60a08301525060c06143f18482850161446d565b60c08301525092915050565b60006040828403121561440f57600080fd5b61441960406156ae565b90506000614341848461446d565b60006040828403121561443957600080fd5b61444360406156ae565b905060006144518484613efb565b82525060206141ca84848301614483565b8051611b49816158df565b8051611b49816158e8565b8035611b49816158f1565b8051611b49816158f1565b8051611b49816158fa565b8051611b4981615903565b8051611b498161590c565b6000602082840312156144c157600080fd5b6000612e258484613bc6565b6000602082840312156144df57600080fd5b6000612e258484613bd1565b600080604083850312156144fe57600080fd5b600061450a8585613bc6565b925050602061451b85828601613bc6565b9150509250929050565b6000806040838503121561453857600080fd5b60006145448585613bc6565b925050602061451b85828601613ef0565b6000806040838503121561456857600080fd5b60006145748585613bc6565b925050602061451b85828601613f55565b60008060008060008060008060006101208a8c0312156145a457600080fd5b60006145b08c8c613bc6565b99505060206145c18c828d01613f55565b98505060406145d28c828d01613f55565b97505060606145e38c828d0161410a565b96505060806145f48c828d0161410a565b95505060a06146058c828d01614478565b94505060c06146168c828d01614478565b93505060e06146278c828d0161410a565b9250506101006146398c828d01613ef0565b9150509295985092959850929598565b6000806040838503121561465c57600080fd5b823567ffffffffffffffff81111561467357600080fd5b61467f85828601613cb8565b925050602083013567ffffffffffffffff81111561469c57600080fd5b61451b85828601613c5b565b6000602082840312156146ba57600080fd5b813567ffffffffffffffff8111156146d157600080fd5b612e2584828501613d2a565b6000602082840312156146ef57600080fd5b815167ffffffffffffffff81111561470657600080fd5b612e2584828501613e80565b6000806000806080858703121561472857600080fd5b845167ffffffffffffffff81111561473f57600080fd5b61474b87828801613e80565b945050602085015167ffffffffffffffff81111561476857600080fd5b61477487828801613bdc565b935050604085015167ffffffffffffffff81111561479157600080fd5b61479d87828801613d9c565b925050606085015167ffffffffffffffff8111156147ba57600080fd5b6147c687828801613e0e565b91505092959194509250565b6000602082840312156147e457600080fd5b6000612e258484613efb565b60006020828403121561480257600080fd5b6000612e258484613f55565b60006020828403121561482057600080fd5b6000612e258484613f60565b60006020828403121561483e57600080fd5b6000612e258484613f81565b60006020828403121561485c57600080fd5b6000612e25848461410a565b60006020828403121561487a57600080fd5b6000612e2584846140d7565b6000806040838503121561489957600080fd5b60006148a585856140d7565b925050602061451b858286016140d7565b6000606082840312156148c857600080fd5b6000612e258484614134565b6000604082840312156148e657600080fd5b6000612e25848461418f565b6000806060838503121561490557600080fd5b6000614911858561418f565b925050604061451b85828601614478565b60008060006080848603121561493757600080fd5b6000614943868661418f565b935050604061495486828701614478565b925050606061496586828701614478565b9150509250925092565b6000610200828403121561498257600080fd5b6000612e2584846141d6565b6000806000806102a085870312156149a557600080fd5b60006149b187876141d6565b9450506102006149c387828801614134565b9350506102606149d5878288016140d7565b9250506102806147c6878288016140d7565b6000604082840312156149f957600080fd5b6000612e258484614317565b600060e08284031215614a1757600080fd5b6000612e258484614352565b600060408284031215614a3557600080fd5b6000612e2584846143fd565b600060408284031215614a5357600080fd5b6000612e258484614427565b600060208284031215614a7157600080fd5b6000612e258484614478565b600060208284031215614a8f57600080fd5b6000612e258484614483565b60008060408385031215614aae57600080fd5b600061450a8585614478565b60008060408385031215614acd57600080fd5b60006145448585614478565b60008060408385031215614aec57600080fd5b60006145748585614478565b60008060408385031215614b0b57600080fd5b6000614b178585614478565b925050602061451b8582860161410a565b60008060408385031215614b3b57600080fd5b6000614b478585614478565b925050602061451b85828601614478565b6000614b648383614bb0565b505060200190565b6000611b468383614ef4565b6000614b848383615037565b505060400190565b6000614b848383615134565b6000614b8483836151f0565b6000614b648383615213565b614bb98161574f565b82525050565b6000614bca82615742565b614bd48185615746565b9350614bdf8361573c565b8060005b83811015614c0d578151614bf78882614b58565b9750614c028361573c565b925050600101614be3565b509495945050505050565b6000614c2382615742565b614c2d8185615746565b935083602082028501614c3f8561573c565b8060005b85811015614c795784840389528151614c5c8582614b6c565b9450614c678361573c565b60209a909a0199925050600101614c43565b5091979650505050505050565b6000614c9182615742565b614c9b8185615746565b9350614ca68361573c565b8060005b83811015614c0d578151614cbe8882614b78565b9750614cc98361573c565b925050600101614caa565b6000614cdf82615742565b614ce98185615746565b9350614cf48361573c565b8060005b83811015614c0d578151614d0c8882614b8c565b9750614d178361573c565b925050600101614cf8565b6000614d2d82615742565b614d378185615746565b9350614d428361573c565b8060005b83811015614c0d578151614d5a8882614b98565b9750614d658361573c565b925050600101614d46565b6000614d7b82615742565b614d858185615746565b9350614d908361573c565b8060005b83811015614c0d578151614da88882614ba4565b9750614db38361573c565b925050600101614d94565b614bb98161575a565b6000614dd282615742565b614ddc8185615746565b9350614dec818560208601615820565b614df58161584c565b9093019392505050565b614bb98161575f565b614bb9816157f3565b614bb9816157fe565b614bb981615809565b6000614e30602683615746565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000614e8f602083615746565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000614ec8601f83615746565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b8051600090610160840190614f098582614e08565b506020830151614f1c6020860182615213565b506040830151614f2f6040860182614f9f565b506060830151614f4260c0860182615213565b506080830151614f5560e0860182615213565b5060a0830151614f69610100860182614bb0565b5060c0830151614f7d610120860182615213565b5060e0830151848203610140860152614f968282614dc7565b95945050505050565b80516080830190614fb08482614dbe565b506020820151614fc36020850182614e11565b506040820151614fd66040850182614e11565b506060820151614fe96060850182615213565b50505050565b80516020830190614fe98482615213565b805160608301906150118482615201565b5060208201516150246020850182615201565b506040820151614fe9604085018261521c565b805160408301906150488482614bb0565b506020820151614fe96020850182615213565b805161020083019061506d8482614bb0565b5060208201516150806020850182614dbe565b50604082015161509360408501826151df565b5060608201516150a66080850182615000565b5060808201516150b960e0850182614dff565b5060a08201516150cd610100850182614dff565b5060c08201516150e1610120850182614fef565b5060e08201516150f5610140850182614fef565b5061010082015161510a6101608501826151f0565b5061012082015161511f6101a08501826151f0565b50610140820151614fe96101e0850182614fef565b805160408301906151458482614dbe565b506020820151614fe9602085018261520a565b805160e08301906151698482615225565b50602082015161517c6020850182615225565b50604082015161518f6040850182615225565b5060608201516151a26060850182615225565b5060808201516151b56080850182615225565b5060a08201516151c860a085018261522e565b5060c0820151614fe960c085018261520a565b9052565b80516040830190615145848261520a565b805160408301906150488482614dbe565b614bb981615788565b614bb98161579b565b614bb9816157c9565b614bb9816157cc565b614bb9816157d5565b614bb9816157e2565b60208101611b498284614bb0565b604081016152538285614bb0565b6116176020830184614dbe565b60208082528101611b468184614d70565b608080825281016152828187614d70565b905081810360208301526152968186614bbf565b905081810360408301526152aa8185614cd4565b905081810360608301526152be8184614d22565b9695505050505050565b60208101611b498284614dbe565b60208101611b498284614dff565b60208101611b498284614e1a565b60208082528101610c9681614e23565b60208082528101610c9681614e82565b60208082528101610c9681614ebb565b60208101611b498284614fef565b6040810161533e8285614fef565b6116176020830184614fef565b60608101611b498284615000565b6102008101611b49828461505b565b6102a08101615377828761505b565b615385610200830186615000565b615393610260830185614fef565b614f96610280830184614fef565b60408101611b498284615134565b60e08101611b498284615158565b60208101611b4982846151db565b604081016153d982856151db565b6116176020830184614bb0565b606081016153f482866151db565b6154016020830185614bb0565b612e256040830184614bb0565b6060810161541c82866151db565b6154296020830185614bb0565b612e256040830184614dbe565b6060810161544482866151db565b6154516020830185614bb0565b612e256040830184614dff565b610140810161546d828d6151db565b61547a602083018c614bb0565b615487604083018b614dff565b615494606083018a614dff565b6154a16080830189614fef565b6154ae60a0830188614fef565b6154bb60c0830187615213565b6154c860e0830186615213565b6154d6610100830185614fef565b6154e4610120830184614dbe565b9b9a5050505050505050505050565b6060810161550182866151db565b81810360208301526155138185614c86565b90508181036040830152614f968184614c18565b6040810161553582856151db565b6116176020830184614dff565b6040810161533e82856151db565b6060810161555e82856151db565b6116176020830184615037565b6080810161557982866151db565b6155866020830185615037565b612e256060830184615213565b60a081016155a182876151db565b6155ae6020830186615037565b6155bb6060830185615213565b614f966080830184615213565b604081016155d682856151db565b6116176020830184615213565b606081016155f182866151db565b6154016020830185615213565b6060810161560c82866151db565b6154296020830185615213565b6060810161562782866151db565b6154516020830185615213565b6060810161564282866151db565b61564f6020830185615213565b612e256040830184614fef565b6060810161566a82866151db565b6156776020830185615213565b612e256040830184615213565b60408101611b4982846151df565b60408101611b4982846151f0565b60208101611b498284615213565b60405181810167ffffffffffffffff811182821017156156cd57600080fd5b604052919050565b600067ffffffffffffffff8211156156ec57600080fd5b5060209081020190565b600067ffffffffffffffff82111561570d57600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b90815260200190565b6000610c96826157b0565b151590565b6000610c968261574f565b80610c9981615874565b80610c998161587e565b80610c9981615888565b6dffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b63ffffffff1690565b67ffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b6000610c968261576a565b6000610c9682615774565b6000610c968261577e565b82818337506000910152565b60005b8381101561583b578181015183820152602001615823565b83811115614fe95750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690565b6009811061393957fe5b6002811061393957fe5b6003811061393957fe5b61589b8161574f565b811461393957600080fd5b61589b8161575a565b61589b8161575f565b6009811061393957600080fd5b6002811061393957600080fd5b6003811061393957600080fd5b61589b81615788565b61589b8161579b565b61589b816157c9565b61589b816157cc565b61589b816157d5565b61589b816157e256fea365627a7a72315820c64395773a7b5a30ad260a8ffa24ee15bf3b0f82191b0bf84f576e35341ba3136c6578706572696d656e74616cf564736f6c63430005100040

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

0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000001bc16d674ec800000000000000000000000000000000000000000000000000004563918244f400000000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000004b3b4ca85a86c47a098a2240000000000000000000000000000000000000000000000000000000000214e8348c4f000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000bcbce7f1b150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000009f7779a8af28de91262ba9d6383fef899102e87600000000000000000000000000000000000000000000000000000000001e8480

-----Decoded View---------------
Arg [0] : riskLimits (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [1] : marginRatio (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [2] : liquidationSpread (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [3] : earningsRate (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [4] : minBorrowedValue (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : accountMaxNumberOfMarketsWithBalances (uint256): 32
Arg [6] : oracleSentinel (address): 0x9F7779A8aF28De91262ba9d6383fEf899102E876
Arg [7] : callbackGasLimit (uint256): 2000000

-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000001bc16d674ec80000
Arg [1] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [2] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [3] : 0000000000000000000000000000000000000000000000001bc16d674ec80000
Arg [4] : 0000000000000000000000000000000000000000000000004563918244f40000
Arg [5] : 0000000000000000000000000000000000000000000000056bc75e2d63100000
Arg [6] : 000000000000000000000000000000004b3b4ca85a86c47a098a224000000000
Arg [7] : 0000000000000000000000000000000000000000000000000214e8348c4f0000
Arg [8] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [9] : 0000000000000000000000000000000000000000000000000bcbce7f1b150000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [12] : 0000000000000000000000009f7779a8af28de91262ba9d6383fef899102e876
Arg [13] : 00000000000000000000000000000000000000000000000000000000001e8480


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

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