ETH Price: $3,958.78 (+1.60%)

Token

ERC-20: Leveraged ETH 3/15 (LETH 3/15)
 

Overview

Max Total Supply

0.101069198970270197 LETH 3/15

Holders

47

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
0.000000000000000001 LETH 3/15

Value
$0.00
0xa7f3dfed2bcf7b35a8824e11ae8f723650edfb58
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

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

Contract Name:
ERC20CappedLong

Compiler Version
v0.4.24+commit.e67f0147

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2018-11-19
*/

pragma solidity 0.4.24;
pragma experimental "v0.5.0";

/*

    Copyright 2018 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.

*/

// File: openzeppelin-solidity/contracts/math/SafeMath.sol

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (_a == 0) {
      return 0;
    }

    c = _a * _b;
    assert(c / _a == _b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
    // assert(_b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = _a / _b;
    // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
    return _a / _b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
    assert(_b <= _a);
    return _a - _b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
    c = _a + _b;
    assert(c >= _a);
    return c;
  }
}

// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol

/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * See https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  function totalSupply() public view returns (uint256);
  function balanceOf(address _who) public view returns (uint256);
  function transfer(address _to, uint256 _value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}

// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  function allowance(address _owner, address _spender)
    public view returns (uint256);

  function transferFrom(address _from, address _to, uint256 _value)
    public returns (bool);

  function approve(address _spender, uint256 _value) public returns (bool);
  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}

// File: openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol

/**
 * @title DetailedERC20 token
 * @dev The decimals are only for visualization purposes.
 * All the operations are done using the smallest and indivisible token unit,
 * just as on Ethereum all the operations are done in wei.
 */
contract DetailedERC20 is ERC20 {
  string public name;
  string public symbol;
  uint8 public decimals;

  constructor(string _name, string _symbol, uint8 _decimals) public {
    name = _name;
    symbol = _symbol;
    decimals = _decimals;
  }
}

// File: openzeppelin-solidity/contracts/ownership/Ownable.sol

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;

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

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipRenounced(owner);
    owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function transferOwnership(address _newOwner) public onlyOwner {
    _transferOwnership(_newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address _newOwner) internal {
    require(_newOwner != address(0));
    emit OwnershipTransferred(owner, _newOwner);
    owner = _newOwner;
  }
}

// File: openzeppelin-solidity/contracts/math/Math.sol

/**
 * @title Math
 * @dev Assorted math operations
 */
library Math {
  function max64(uint64 _a, uint64 _b) internal pure returns (uint64) {
    return _a >= _b ? _a : _b;
  }

  function min64(uint64 _a, uint64 _b) internal pure returns (uint64) {
    return _a < _b ? _a : _b;
  }

  function max256(uint256 _a, uint256 _b) internal pure returns (uint256) {
    return _a >= _b ? _a : _b;
  }

  function min256(uint256 _a, uint256 _b) internal pure returns (uint256) {
    return _a < _b ? _a : _b;
  }
}

// File: openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol

/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) internal balances;

  uint256 internal totalSupply_;

  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }

  /**
  * @dev Transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_value <= balances[msg.sender]);
    require(_to != address(0));

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256) {
    return balances[_owner];
  }

}

// File: openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://github.com/ethereum/EIPs/issues/20
 * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {

  mapping (address => mapping (address => uint256)) internal allowed;

  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  )
    public
    returns (bool)
  {
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);
    require(_to != address(0));

    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    emit Transfer(_from, _to, _value);
    return true;
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * 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
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    return true;
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(
    address _owner,
    address _spender
   )
    public
    view
    returns (uint256)
  {
    return allowed[_owner][_spender];
  }

  /**
   * @dev Increase the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param _spender The address which will spend the funds.
   * @param _addedValue The amount of tokens to increase the allowance by.
   */
  function increaseApproval(
    address _spender,
    uint256 _addedValue
  )
    public
    returns (bool)
  {
    allowed[msg.sender][_spender] = (
      allowed[msg.sender][_spender].add(_addedValue));
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

  /**
   * @dev Decrease the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed[_spender] == 0. To decrement
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param _spender The address which will spend the funds.
   * @param _subtractedValue The amount of tokens to decrease the allowance by.
   */
  function decreaseApproval(
    address _spender,
    uint256 _subtractedValue
  )
    public
    returns (bool)
  {
    uint256 oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue >= oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

}

// File: contracts/lib/AccessControlledBase.sol

/**
 * @title AccessControlledBase
 * @author dYdX
 *
 * Base functionality for access control. Requires an implementation to
 * provide a way to grant and optionally revoke access
 */
contract AccessControlledBase {
    // ============ State Variables ============

    mapping (address => bool) public authorized;

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

    event AccessGranted(
        address who
    );

    event AccessRevoked(
        address who
    );

    // ============ Modifiers ============

    modifier requiresAuthorization() {
        require(
            authorized[msg.sender],
            "AccessControlledBase#requiresAuthorization: Sender not authorized"
        );
        _;
    }
}

// File: contracts/lib/StaticAccessControlled.sol

/**
 * @title StaticAccessControlled
 * @author dYdX
 *
 * Allows for functions to be access controled
 * Permissions cannot be changed after a grace period
 */
contract StaticAccessControlled is AccessControlledBase, Ownable {
    using SafeMath for uint256;

    // ============ State Variables ============

    // Timestamp after which no additional access can be granted
    uint256 public GRACE_PERIOD_EXPIRATION;

    // ============ Constructor ============

    constructor(
        uint256 gracePeriod
    )
        public
        Ownable()
    {
        GRACE_PERIOD_EXPIRATION = block.timestamp.add(gracePeriod);
    }

    // ============ Owner-Only State-Changing Functions ============

    function grantAccess(
        address who
    )
        external
        onlyOwner
    {
        require(
            block.timestamp < GRACE_PERIOD_EXPIRATION,
            "StaticAccessControlled#grantAccess: Cannot grant access after grace period"
        );

        emit AccessGranted(who);
        authorized[who] = true;
    }
}

// File: contracts/lib/GeneralERC20.sol

/**
 * @title GeneralERC20
 * @author dYdX
 *
 * Interface for using ERC20 Tokens. We have to use a special interface to call ERC20 functions so
 * that we dont automatically revert when calling non-compliant tokens that have no return value for
 * transfer(), transferFrom(), or approve().
 */
interface GeneralERC20 {
    function totalSupply(
    )
        external
        view
        returns (uint256);

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

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

    function transfer(
        address to,
        uint256 value
    )
        external;

    function transferFrom(
        address from,
        address to,
        uint256 value
    )
        external;

    function approve(
        address spender,
        uint256 value
    )
        external;
}

// File: contracts/lib/TokenInteract.sol

/**
 * @title TokenInteract
 * @author dYdX
 *
 * This library contains basic functions for interacting with ERC20 tokens
 */
library TokenInteract {
    function balanceOf(
        address token,
        address owner
    )
        internal
        view
        returns (uint256)
    {
        return GeneralERC20(token).balanceOf(owner);
    }

    function allowance(
        address token,
        address owner,
        address spender
    )
        internal
        view
        returns (uint256)
    {
        return GeneralERC20(token).allowance(owner, spender);
    }

    function approve(
        address token,
        address spender,
        uint256 amount
    )
        internal
    {
        GeneralERC20(token).approve(spender, amount);

        require(
            checkSuccess(),
            "TokenInteract#approve: Approval failed"
        );
    }

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

        GeneralERC20(token).transfer(to, amount);

        require(
            checkSuccess(),
            "TokenInteract#transfer: Transfer failed"
        );
    }

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

        GeneralERC20(token).transferFrom(from, to, amount);

        require(
            checkSuccess(),
            "TokenInteract#transferFrom: TransferFrom failed"
        );
    }

    // ============ Private Helper-Functions ============

    /**
     * Checks the return value of the previous function up to 32 bytes. Returns true if the previous
     * function returned 0 bytes or 32 bytes that are not all-zero.
     */
    function checkSuccess(
    )
        private
        pure
        returns (bool)
    {
        uint256 returnValue = 0;

        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            // check number of bytes returned from last function call
            switch returndatasize

            // no bytes returned: assume success
            case 0x0 {
                returnValue := 1
            }

            // 32 bytes returned: check if non-zero
            case 0x20 {
                // copy 32 bytes into scratch space
                returndatacopy(0x0, 0x0, 0x20)

                // load those bytes into returnValue
                returnValue := mload(0x0)
            }

            // not sure what was returned: dont mark as success
            default { }
        }

        return returnValue != 0;
    }
}

// File: contracts/margin/TokenProxy.sol

/**
 * @title TokenProxy
 * @author dYdX
 *
 * Used to transfer tokens between addresses which have set allowance on this contract.
 */
contract TokenProxy is StaticAccessControlled {
    using SafeMath for uint256;

    // ============ Constructor ============

    constructor(
        uint256 gracePeriod
    )
        public
        StaticAccessControlled(gracePeriod)
    {}

    // ============ Authorized-Only State Changing Functions ============

    /**
     * Transfers tokens from an address (that has set allowance on the proxy) to another address.
     *
     * @param  token  The address of the ERC20 token
     * @param  from   The address to transfer token from
     * @param  to     The address to transfer tokens to
     * @param  value  The number of tokens to transfer
     */
    function transferTokens(
        address token,
        address from,
        address to,
        uint256 value
    )
        external
        requiresAuthorization
    {
        TokenInteract.transferFrom(
            token,
            from,
            to,
            value
        );
    }

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

    /**
     * Getter function to get the amount of token that the proxy is able to move for a particular
     * address. The minimum of 1) the balance of that address and 2) the allowance given to proxy.
     *
     * @param  who    The owner of the tokens
     * @param  token  The address of the ERC20 token
     * @return        The number of tokens able to be moved by the proxy from the address specified
     */
    function available(
        address who,
        address token
    )
        external
        view
        returns (uint256)
    {
        return Math.min256(
            TokenInteract.allowance(token, who, address(this)),
            TokenInteract.balanceOf(token, who)
        );
    }
}

// File: contracts/margin/Vault.sol

/**
 * @title Vault
 * @author dYdX
 *
 * Holds and transfers tokens in vaults denominated by id
 *
 * Vault only supports ERC20 tokens, and will not accept any tokens that require
 * a tokenFallback or equivalent function (See ERC223, ERC777, etc.)
 */
contract Vault is StaticAccessControlled
{
    using SafeMath for uint256;

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

    event ExcessTokensWithdrawn(
        address indexed token,
        address indexed to,
        address caller
    );

    // ============ State Variables ============

    // Address of the TokenProxy contract. Used for moving tokens.
    address public TOKEN_PROXY;

    // Map from vault ID to map from token address to amount of that token attributed to the
    // particular vault ID.
    mapping (bytes32 => mapping (address => uint256)) public balances;

    // Map from token address to total amount of that token attributed to some account.
    mapping (address => uint256) public totalBalances;

    // ============ Constructor ============

    constructor(
        address proxy,
        uint256 gracePeriod
    )
        public
        StaticAccessControlled(gracePeriod)
    {
        TOKEN_PROXY = proxy;
    }

    // ============ Owner-Only State-Changing Functions ============

    /**
     * Allows the owner to withdraw any excess tokens sent to the vault by unconventional means,
     * including (but not limited-to) token airdrops. Any tokens moved to the vault by TOKEN_PROXY
     * will be accounted for and will not be withdrawable by this function.
     *
     * @param  token  ERC20 token address
     * @param  to     Address to transfer tokens to
     * @return        Amount of tokens withdrawn
     */
    function withdrawExcessToken(
        address token,
        address to
    )
        external
        onlyOwner
        returns (uint256)
    {
        uint256 actualBalance = TokenInteract.balanceOf(token, address(this));
        uint256 accountedBalance = totalBalances[token];
        uint256 withdrawableBalance = actualBalance.sub(accountedBalance);

        require(
            withdrawableBalance != 0,
            "Vault#withdrawExcessToken: Withdrawable token amount must be non-zero"
        );

        TokenInteract.transfer(token, to, withdrawableBalance);

        emit ExcessTokensWithdrawn(token, to, msg.sender);

        return withdrawableBalance;
    }

    // ============ Authorized-Only State-Changing Functions ============

    /**
     * Transfers tokens from an address (that has approved the proxy) to the vault.
     *
     * @param  id      The vault which will receive the tokens
     * @param  token   ERC20 token address
     * @param  from    Address from which the tokens will be taken
     * @param  amount  Number of the token to be sent
     */
    function transferToVault(
        bytes32 id,
        address token,
        address from,
        uint256 amount
    )
        external
        requiresAuthorization
    {
        // First send tokens to this contract
        TokenProxy(TOKEN_PROXY).transferTokens(
            token,
            from,
            address(this),
            amount
        );

        // Then increment balances
        balances[id][token] = balances[id][token].add(amount);
        totalBalances[token] = totalBalances[token].add(amount);

        // This should always be true. If not, something is very wrong
        assert(totalBalances[token] >= balances[id][token]);

        validateBalance(token);
    }

    /**
     * Transfers a certain amount of funds to an address.
     *
     * @param  id      The vault from which to send the tokens
     * @param  token   ERC20 token address
     * @param  to      Address to transfer tokens to
     * @param  amount  Number of the token to be sent
     */
    function transferFromVault(
        bytes32 id,
        address token,
        address to,
        uint256 amount
    )
        external
        requiresAuthorization
    {
        // Next line also asserts that (balances[id][token] >= amount);
        balances[id][token] = balances[id][token].sub(amount);

        // Next line also asserts that (totalBalances[token] >= amount);
        totalBalances[token] = totalBalances[token].sub(amount);

        // This should always be true. If not, something is very wrong
        assert(totalBalances[token] >= balances[id][token]);

        // Do the sending
        TokenInteract.transfer(token, to, amount); // asserts transfer succeeded

        // Final validation
        validateBalance(token);
    }

    // ============ Private Helper-Functions ============

    /**
     * Verifies that this contract is in control of at least as many tokens as accounted for
     *
     * @param  token  Address of ERC20 token
     */
    function validateBalance(
        address token
    )
        private
        view
    {
        // The actual balance could be greater than totalBalances[token] because anyone
        // can send tokens to the contract's address which cannot be accounted for
        assert(TokenInteract.balanceOf(token, address(this)) >= totalBalances[token]);
    }
}

// File: contracts/lib/ReentrancyGuard.sol

/**
 * @title ReentrancyGuard
 * @author dYdX
 *
 * Optimized version of the well-known ReentrancyGuard contract
 */
contract ReentrancyGuard {
    uint256 private _guardCounter = 1;

    modifier nonReentrant() {
        uint256 localCounter = _guardCounter + 1;
        _guardCounter = localCounter;
        _;
        require(
            _guardCounter == localCounter,
            "Reentrancy check failure"
        );
    }
}

// File: openzeppelin-solidity/contracts/AddressUtils.sol

/**
 * Utility library of inline functions on addresses
 */
library AddressUtils {

  /**
   * Returns whether the target address is a contract
   * @dev This function will return false if invoked during the constructor of a contract,
   * as the code is not actually created until after the constructor finishes.
   * @param _addr address to check
   * @return whether the target address is a contract
   */
  function isContract(address _addr) internal view returns (bool) {
    uint256 size;
    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603
    // for more details about how this works.
    // TODO Check this again before the Serenity release, because all addresses will be
    // contracts then.
    // solium-disable-next-line security/no-inline-assembly
    assembly { size := extcodesize(_addr) }
    return size > 0;
  }

}

// File: contracts/lib/Fraction.sol

/**
 * @title Fraction
 * @author dYdX
 *
 * This library contains implementations for fraction structs.
 */
library Fraction {
    struct Fraction128 {
        uint128 num;
        uint128 den;
    }
}

// File: contracts/lib/FractionMath.sol

/**
 * @title FractionMath
 * @author dYdX
 *
 * This library contains safe math functions for manipulating fractions.
 */
library FractionMath {
    using SafeMath for uint256;
    using SafeMath for uint128;

    /**
     * Returns a Fraction128 that is equal to a + b
     *
     * @param  a  The first Fraction128
     * @param  b  The second Fraction128
     * @return    The result (sum)
     */
    function add(
        Fraction.Fraction128 memory a,
        Fraction.Fraction128 memory b
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        uint256 left = a.num.mul(b.den);
        uint256 right = b.num.mul(a.den);
        uint256 denominator = a.den.mul(b.den);

        // if left + right overflows, prevent overflow
        if (left + right < left) {
            left = left.div(2);
            right = right.div(2);
            denominator = denominator.div(2);
        }

        return bound(left.add(right), denominator);
    }

    /**
     * Returns a Fraction128 that is equal to a - (1/2)^d
     *
     * @param  a  The Fraction128
     * @param  d  The power of (1/2)
     * @return    The result
     */
    function sub1Over(
        Fraction.Fraction128 memory a,
        uint128 d
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        if (a.den % d == 0) {
            return bound(
                a.num.sub(a.den.div(d)),
                a.den
            );
        }
        return bound(
            a.num.mul(d).sub(a.den),
            a.den.mul(d)
        );
    }

    /**
     * Returns a Fraction128 that is equal to a / d
     *
     * @param  a  The first Fraction128
     * @param  d  The divisor
     * @return    The result (quotient)
     */
    function div(
        Fraction.Fraction128 memory a,
        uint128 d
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        if (a.num % d == 0) {
            return bound(
                a.num.div(d),
                a.den
            );
        }
        return bound(
            a.num,
            a.den.mul(d)
        );
    }

    /**
     * Returns a Fraction128 that is equal to a * b.
     *
     * @param  a  The first Fraction128
     * @param  b  The second Fraction128
     * @return    The result (product)
     */
    function mul(
        Fraction.Fraction128 memory a,
        Fraction.Fraction128 memory b
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        return bound(
            a.num.mul(b.num),
            a.den.mul(b.den)
        );
    }

    /**
     * Returns a fraction from two uint256's. Fits them into uint128 if necessary.
     *
     * @param  num  The numerator
     * @param  den  The denominator
     * @return      The Fraction128 that matches num/den most closely
     */
    /* solium-disable-next-line security/no-assign-params */
    function bound(
        uint256 num,
        uint256 den
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        uint256 max = num > den ? num : den;
        uint256 first128Bits = (max >> 128);
        if (first128Bits != 0) {
            first128Bits += 1;
            num /= first128Bits;
            den /= first128Bits;
        }

        assert(den != 0); // coverage-enable-line
        assert(den < 2**128);
        assert(num < 2**128);

        return Fraction.Fraction128({
            num: uint128(num),
            den: uint128(den)
        });
    }

    /**
     * Returns an in-memory copy of a Fraction128
     *
     * @param  a  The Fraction128 to copy
     * @return    A copy of the Fraction128
     */
    function copy(
        Fraction.Fraction128 memory a
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        validate(a);
        return Fraction.Fraction128({ num: a.num, den: a.den });
    }

    // ============ Private Helper-Functions ============

    /**
     * Asserts that a Fraction128 is valid (i.e. the denominator is non-zero)
     *
     * @param  a  The Fraction128 to validate
     */
    function validate(
        Fraction.Fraction128 memory a
    )
        private
        pure
    {
        assert(a.den != 0); // coverage-enable-line
    }
}

// File: contracts/lib/Exponent.sol

/**
 * @title Exponent
 * @author dYdX
 *
 * This library contains an implementation for calculating e^X for arbitrary fraction X
 */
library Exponent {
    using SafeMath for uint256;
    using FractionMath for Fraction.Fraction128;

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

    // 2**128 - 1
    uint128 constant public MAX_NUMERATOR = 340282366920938463463374607431768211455;

    // Number of precomputed integers, X, for E^((1/2)^X)
    uint256 constant public MAX_PRECOMPUTE_PRECISION = 32;

    // Number of precomputed integers, X, for E^X
    uint256 constant public NUM_PRECOMPUTED_INTEGERS = 32;

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

    /**
     * Returns e^X for any fraction X
     *
     * @param  X                    The exponent
     * @param  precomputePrecision  Accuracy of precomputed terms
     * @param  maclaurinPrecision   Accuracy of Maclaurin terms
     * @return                      e^X
     */
    function exp(
        Fraction.Fraction128 memory X,
        uint256 precomputePrecision,
        uint256 maclaurinPrecision
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        require(
            precomputePrecision <= MAX_PRECOMPUTE_PRECISION,
            "Exponent#exp: Precompute precision over maximum"
        );

        Fraction.Fraction128 memory Xcopy = X.copy();
        if (Xcopy.num == 0) { // e^0 = 1
            return ONE();
        }

        // get the integer value of the fraction (example: 9/4 is 2.25 so has integerValue of 2)
        uint256 integerX = uint256(Xcopy.num).div(Xcopy.den);

        // if X is less than 1, then just calculate X
        if (integerX == 0) {
            return expHybrid(Xcopy, precomputePrecision, maclaurinPrecision);
        }

        // get e^integerX
        Fraction.Fraction128 memory expOfInt =
            getPrecomputedEToThe(integerX % NUM_PRECOMPUTED_INTEGERS);
        while (integerX >= NUM_PRECOMPUTED_INTEGERS) {
            expOfInt = expOfInt.mul(getPrecomputedEToThe(NUM_PRECOMPUTED_INTEGERS));
            integerX -= NUM_PRECOMPUTED_INTEGERS;
        }

        // multiply e^integerX by e^decimalX
        Fraction.Fraction128 memory decimalX = Fraction.Fraction128({
            num: Xcopy.num % Xcopy.den,
            den: Xcopy.den
        });
        return expHybrid(decimalX, precomputePrecision, maclaurinPrecision).mul(expOfInt);
    }

    /**
     * Returns e^X for any X < 1. Multiplies precomputed values to get close to the real value, then
     * Maclaurin Series approximation to reduce error.
     *
     * @param  X                    Exponent
     * @param  precomputePrecision  Accuracy of precomputed terms
     * @param  maclaurinPrecision   Accuracy of Maclaurin terms
     * @return                      e^X
     */
    function expHybrid(
        Fraction.Fraction128 memory X,
        uint256 precomputePrecision,
        uint256 maclaurinPrecision
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        assert(precomputePrecision <= MAX_PRECOMPUTE_PRECISION);
        assert(X.num < X.den);
        // will also throw if precomputePrecision is larger than the array length in getDenominator

        Fraction.Fraction128 memory Xtemp = X.copy();
        if (Xtemp.num == 0) { // e^0 = 1
            return ONE();
        }

        Fraction.Fraction128 memory result = ONE();

        uint256 d = 1; // 2^i
        for (uint256 i = 1; i <= precomputePrecision; i++) {
            d *= 2;

            // if Fraction > 1/d, subtract 1/d and multiply result by precomputed e^(1/d)
            if (d.mul(Xtemp.num) >= Xtemp.den) {
                Xtemp = Xtemp.sub1Over(uint128(d));
                result = result.mul(getPrecomputedEToTheHalfToThe(i));
            }
        }
        return result.mul(expMaclaurin(Xtemp, maclaurinPrecision));
    }

    /**
     * Returns e^X for any X, using Maclaurin Series approximation
     *
     * e^X = SUM(X^n / n!) for n >= 0
     * e^X = 1 + X/1! + X^2/2! + X^3/3! ...
     *
     * @param  X           Exponent
     * @param  precision   Accuracy of Maclaurin terms
     * @return             e^X
     */
    function expMaclaurin(
        Fraction.Fraction128 memory X,
        uint256 precision
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        Fraction.Fraction128 memory Xcopy = X.copy();
        if (Xcopy.num == 0) { // e^0 = 1
            return ONE();
        }

        Fraction.Fraction128 memory result = ONE();
        Fraction.Fraction128 memory Xtemp = ONE();
        for (uint256 i = 1; i <= precision; i++) {
            Xtemp = Xtemp.mul(Xcopy.div(uint128(i)));
            result = result.add(Xtemp);
        }
        return result;
    }

    /**
     * Returns a fraction roughly equaling E^((1/2)^x) for integer x
     */
    function getPrecomputedEToTheHalfToThe(
        uint256 x
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        assert(x <= MAX_PRECOMPUTE_PRECISION);

        uint128 denominator = [
            125182886983370532117250726298150828301,
            206391688497133195273760705512282642279,
            265012173823417992016237332255925138361,
            300298134811882980317033350418940119802,
            319665700530617779809390163992561606014,
            329812979126047300897653247035862915816,
            335006777809430963166468914297166288162,
            337634268532609249517744113622081347950,
            338955731696479810470146282672867036734,
            339618401537809365075354109784799900812,
            339950222128463181389559457827561204959,
            340116253979683015278260491021941090650,
            340199300311581465057079429423749235412,
            340240831081268226777032180141478221816,
            340261598367316729254995498374473399540,
            340271982485676106947851156443492415142,
            340277174663693808406010255284800906112,
            340279770782412691177936847400746725466,
            340281068849199706686796915841848278311,
            340281717884450116236033378667952410919,
            340282042402539547492367191008339680733,
            340282204661700319870089970029119685699,
            340282285791309720262481214385569134454,
            340282326356121674011576912006427792656,
            340282346638529464274601981200276914173,
            340282356779733812753265346086924801364,
            340282361850336100329388676752133324799,
            340282364385637272451648746721404212564,
            340282365653287865596328444437856608255,
            340282366287113163939555716675618384724,
            340282366604025813553891209601455838559,
            340282366762482138471739420386372790954,
            340282366841710300958333641874363209044
        ][x];
        return Fraction.Fraction128({
            num: MAX_NUMERATOR,
            den: denominator
        });
    }

    /**
     * Returns a fraction roughly equaling E^(x) for integer x
     */
    function getPrecomputedEToThe(
        uint256 x
    )
        internal
        pure
        returns (Fraction.Fraction128 memory)
    {
        assert(x <= NUM_PRECOMPUTED_INTEGERS);

        uint128 denominator = [
            340282366920938463463374607431768211455,
            125182886983370532117250726298150828301,
            46052210507670172419625860892627118820,
            16941661466271327126146327822211253888,
            6232488952727653950957829210887653621,
            2292804553036637136093891217529878878,
            843475657686456657683449904934172134,
            310297353591408453462393329342695980,
            114152017036184782947077973323212575,
            41994180235864621538772677139808695,
            15448795557622704876497742989562086,
            5683294276510101335127414470015662,
            2090767122455392675095471286328463,
            769150240628514374138961856925097,
            282954560699298259527814398449860,
            104093165666968799599694528310221,
            38293735615330848145349245349513,
            14087478058534870382224480725096,
            5182493555688763339001418388912,
            1906532833141383353974257736699,
            701374233231058797338605168652,
            258021160973090761055471434334,
            94920680509187392077350434438,
            34919366901332874995585576427,
            12846117181722897538509298435,
            4725822410035083116489797150,
            1738532907279185132707372378,
            639570514388029575350057932,
            235284843422800231081973821,
            86556456714490055457751527,
            31842340925906738090071268,
            11714142585413118080082437,
            4309392228124372433711936
        ][x];
        return Fraction.Fraction128({
            num: MAX_NUMERATOR,
            den: denominator
        });
    }

    // ============ Private Helper-Functions ============

    function ONE()
        private
        pure
        returns (Fraction.Fraction128 memory)
    {
        return Fraction.Fraction128({ num: 1, den: 1 });
    }
}

// File: contracts/lib/MathHelpers.sol

/**
 * @title MathHelpers
 * @author dYdX
 *
 * This library helps with common math functions in Solidity
 */
library MathHelpers {
    using SafeMath for uint256;

    /**
     * Calculates partial value given a numerator and denominator.
     *
     * @param  numerator    Numerator
     * @param  denominator  Denominator
     * @param  target       Value to calculate partial of
     * @return              target * numerator / denominator
     */
    function getPartialAmount(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256)
    {
        return numerator.mul(target).div(denominator);
    }

    /**
     * Calculates partial value given a numerator and denominator, rounded up.
     *
     * @param  numerator    Numerator
     * @param  denominator  Denominator
     * @param  target       Value to calculate partial of
     * @return              Rounded-up result of target * numerator / denominator
     */
    function getPartialAmountRoundedUp(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256)
    {
        return divisionRoundedUp(numerator.mul(target), denominator);
    }

    /**
     * Calculates division given a numerator and denominator, rounded up.
     *
     * @param  numerator    Numerator.
     * @param  denominator  Denominator.
     * @return              Rounded-up result of numerator / denominator
     */
    function divisionRoundedUp(
        uint256 numerator,
        uint256 denominator
    )
        internal
        pure
        returns (uint256)
    {
        assert(denominator != 0); // coverage-enable-line
        if (numerator == 0) {
            return 0;
        }
        return numerator.sub(1).div(denominator).add(1);
    }

    /**
     * Calculates and returns the maximum value for a uint256 in solidity
     *
     * @return  The maximum value for uint256
     */
    function maxUint256(
    )
        internal
        pure
        returns (uint256)
    {
        return 2 ** 256 - 1;
    }

    /**
     * Calculates and returns the maximum value for a uint256 in solidity
     *
     * @return  The maximum value for uint256
     */
    function maxUint32(
    )
        internal
        pure
        returns (uint32)
    {
        return 2 ** 32 - 1;
    }

    /**
     * Returns the number of bits in a uint256. That is, the lowest number, x, such that n >> x == 0
     *
     * @param  n  The uint256 to get the number of bits in
     * @return    The number of bits in n
     */
    function getNumBits(
        uint256 n
    )
        internal
        pure
        returns (uint256)
    {
        uint256 first = 0;
        uint256 last = 256;
        while (first < last) {
            uint256 check = (first + last) / 2;
            if ((n >> check) == 0) {
                last = check;
            } else {
                first = check + 1;
            }
        }
        assert(first <= 256);
        return first;
    }
}

// File: contracts/margin/impl/InterestImpl.sol

/**
 * @title InterestImpl
 * @author dYdX
 *
 * A library that calculates continuously compounded interest for principal, time period, and
 * interest rate.
 */
library InterestImpl {
    using SafeMath for uint256;
    using FractionMath for Fraction.Fraction128;

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

    uint256 constant DEFAULT_PRECOMPUTE_PRECISION = 11;

    uint256 constant DEFAULT_MACLAURIN_PRECISION = 5;

    uint256 constant MAXIMUM_EXPONENT = 80;

    uint128 constant E_TO_MAXIUMUM_EXPONENT = 55406223843935100525711733958316613;

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

    /**
     * Returns total tokens owed after accruing interest. Continuously compounding and accurate to
     * roughly 10^18 decimal places. Continuously compounding interest follows the formula:
     * I = P * e^(R*T)
     *
     * @param  principal           Principal of the interest calculation
     * @param  interestRate        Annual nominal interest percentage times 10**6.
     *                             (example: 5% = 5e6)
     * @param  secondsOfInterest   Number of seconds that interest has been accruing
     * @return                     Total amount of tokens owed. Greater than tokenAmount.
     */
    function getCompoundedInterest(
        uint256 principal,
        uint256 interestRate,
        uint256 secondsOfInterest
    )
        public
        pure
        returns (uint256)
    {
        uint256 numerator = interestRate.mul(secondsOfInterest);
        uint128 denominator = (10**8) * (365 * 1 days);

        // interestRate and secondsOfInterest should both be uint32
        assert(numerator < 2**128);

        // fraction representing (Rate * Time)
        Fraction.Fraction128 memory rt = Fraction.Fraction128({
            num: uint128(numerator),
            den: denominator
        });

        // calculate e^(RT)
        Fraction.Fraction128 memory eToRT;
        if (numerator.div(denominator) >= MAXIMUM_EXPONENT) {
            // degenerate case: cap calculation
            eToRT = Fraction.Fraction128({
                num: E_TO_MAXIUMUM_EXPONENT,
                den: 1
            });
        } else {
            // normal case: calculate e^(RT)
            eToRT = Exponent.exp(
                rt,
                DEFAULT_PRECOMPUTE_PRECISION,
                DEFAULT_MACLAURIN_PRECISION
            );
        }

        // e^X for positive X should be greater-than or equal to 1
        assert(eToRT.num >= eToRT.den);

        return safeMultiplyUint256ByFraction(principal, eToRT);
    }

    // ============ Private Helper-Functions ============

    /**
     * Returns n * f, trying to prevent overflow as much as possible. Assumes that the numerator
     * and denominator of f are less than 2**128.
     */
    function safeMultiplyUint256ByFraction(
        uint256 n,
        Fraction.Fraction128 memory f
    )
        private
        pure
        returns (uint256)
    {
        uint256 term1 = n.div(2 ** 128); // first 128 bits
        uint256 term2 = n % (2 ** 128); // second 128 bits

        // uncommon scenario, requires n >= 2**128. calculates term1 = term1 * f
        if (term1 > 0) {
            term1 = term1.mul(f.num);
            uint256 numBits = MathHelpers.getNumBits(term1);

            // reduce rounding error by shifting all the way to the left before dividing
            term1 = MathHelpers.divisionRoundedUp(
                term1 << (uint256(256).sub(numBits)),
                f.den);

            // continue shifting or reduce shifting to get the right number
            if (numBits > 128) {
                term1 = term1 << (numBits.sub(128));
            } else if (numBits < 128) {
                term1 = term1 >> (uint256(128).sub(numBits));
            }
        }

        // calculates term2 = term2 * f
        term2 = MathHelpers.getPartialAmountRoundedUp(
            f.num,
            f.den,
            term2
        );

        return term1.add(term2);
    }
}

// File: contracts/margin/impl/MarginState.sol

/**
 * @title MarginState
 * @author dYdX
 *
 * Contains state for the Margin contract. Also used by libraries that implement Margin functions.
 */
library MarginState {
    struct State {
        // Address of the Vault contract
        address VAULT;

        // Address of the TokenProxy contract
        address TOKEN_PROXY;

        // Mapping from loanHash -> amount, which stores the amount of a loan which has
        // already been filled.
        mapping (bytes32 => uint256) loanFills;

        // Mapping from loanHash -> amount, which stores the amount of a loan which has
        // already been canceled.
        mapping (bytes32 => uint256) loanCancels;

        // Mapping from positionId -> Position, which stores all the open margin positions.
        mapping (bytes32 => MarginCommon.Position) positions;

        // Mapping from positionId -> bool, which stores whether the position has previously been
        // open, but is now closed.
        mapping (bytes32 => bool) closedPositions;

        // Mapping from positionId -> uint256, which stores the total amount of owedToken that has
        // ever been repaid to the lender for each position. Does not reset.
        mapping (bytes32 => uint256) totalOwedTokenRepaidToLender;
    }
}

// File: contracts/margin/interfaces/lender/LoanOwner.sol

/**
 * @title LoanOwner
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to own loans on behalf of other accounts.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface LoanOwner {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to receive ownership of a loan sell via the
     * transferLoan function or the atomic-assign to the "owner" field in a loan offering.
     *
     * @param  from        Address of the previous owner
     * @param  positionId  Unique ID of the position
     * @return             This address to keep ownership, a different address to pass-on ownership
     */
    function receiveLoanOwnership(
        address from,
        bytes32 positionId
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/interfaces/owner/PositionOwner.sol

/**
 * @title PositionOwner
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to own position on behalf of other
 * accounts
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface PositionOwner {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to receive ownership of a position via the
     * transferPosition function or the atomic-assign to the "owner" field when opening a position.
     *
     * @param  from        Address of the previous owner
     * @param  positionId  Unique ID of the position
     * @return             This address to keep ownership, a different address to pass-on ownership
     */
    function receivePositionOwnership(
        address from,
        bytes32 positionId
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/TransferInternal.sol

/**
 * @title TransferInternal
 * @author dYdX
 *
 * This library contains the implementation for transferring ownership of loans and positions.
 */
library TransferInternal {

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

    /**
     * Ownership of a loan was transferred to a new address
     */
    event LoanTransferred(
        bytes32 indexed positionId,
        address indexed from,
        address indexed to
    );

    /**
     * Ownership of a postion was transferred to a new address
     */
    event PositionTransferred(
        bytes32 indexed positionId,
        address indexed from,
        address indexed to
    );

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

    /**
     * Returns either the address of the new loan owner, or the address to which they wish to
     * pass ownership of the loan. This function does not actually set the state of the position
     *
     * @param  positionId  The Unique ID of the position
     * @param  oldOwner    The previous owner of the loan
     * @param  newOwner    The intended owner of the loan
     * @return             The address that the intended owner wishes to assign the loan to (may be
     *                     the same as the intended owner).
     */
    function grantLoanOwnership(
        bytes32 positionId,
        address oldOwner,
        address newOwner
    )
        internal
        returns (address)
    {
        // log event except upon position creation
        if (oldOwner != address(0)) {
            emit LoanTransferred(positionId, oldOwner, newOwner);
        }

        if (AddressUtils.isContract(newOwner)) {
            address nextOwner =
                LoanOwner(newOwner).receiveLoanOwnership(oldOwner, positionId);
            if (nextOwner != newOwner) {
                return grantLoanOwnership(positionId, newOwner, nextOwner);
            }
        }

        require(
            newOwner != address(0),
            "TransferInternal#grantLoanOwnership: New owner did not consent to owning loan"
        );

        return newOwner;
    }

    /**
     * Returns either the address of the new position owner, or the address to which they wish to
     * pass ownership of the position. This function does not actually set the state of the position
     *
     * @param  positionId  The Unique ID of the position
     * @param  oldOwner    The previous owner of the position
     * @param  newOwner    The intended owner of the position
     * @return             The address that the intended owner wishes to assign the position to (may
     *                     be the same as the intended owner).
     */
    function grantPositionOwnership(
        bytes32 positionId,
        address oldOwner,
        address newOwner
    )
        internal
        returns (address)
    {
        // log event except upon position creation
        if (oldOwner != address(0)) {
            emit PositionTransferred(positionId, oldOwner, newOwner);
        }

        if (AddressUtils.isContract(newOwner)) {
            address nextOwner =
                PositionOwner(newOwner).receivePositionOwnership(oldOwner, positionId);
            if (nextOwner != newOwner) {
                return grantPositionOwnership(positionId, newOwner, nextOwner);
            }
        }

        require(
            newOwner != address(0),
            "TransferInternal#grantPositionOwnership: New owner did not consent to owning position"
        );

        return newOwner;
    }
}

// File: contracts/lib/TimestampHelper.sol

/**
 * @title TimestampHelper
 * @author dYdX
 *
 * Helper to get block timestamps in other formats
 */
library TimestampHelper {
    function getBlockTimestamp32()
        internal
        view
        returns (uint32)
    {
        // Should not still be in-use in the year 2106
        assert(uint256(uint32(block.timestamp)) == block.timestamp);

        assert(block.timestamp > 0);

        return uint32(block.timestamp);
    }
}

// File: contracts/margin/impl/MarginCommon.sol

/**
 * @title MarginCommon
 * @author dYdX
 *
 * This library contains common functions for implementations of public facing Margin functions
 */
library MarginCommon {
    using SafeMath for uint256;

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

    struct Position {
        address owedToken;       // Immutable
        address heldToken;       // Immutable
        address lender;
        address owner;
        uint256 principal;
        uint256 requiredDeposit;
        uint32  callTimeLimit;   // Immutable
        uint32  startTimestamp;  // Immutable, cannot be 0
        uint32  callTimestamp;
        uint32  maxDuration;     // Immutable
        uint32  interestRate;    // Immutable
        uint32  interestPeriod;  // Immutable
    }

    struct LoanOffering {
        address   owedToken;
        address   heldToken;
        address   payer;
        address   owner;
        address   taker;
        address   positionOwner;
        address   feeRecipient;
        address   lenderFeeToken;
        address   takerFeeToken;
        LoanRates rates;
        uint256   expirationTimestamp;
        uint32    callTimeLimit;
        uint32    maxDuration;
        uint256   salt;
        bytes32   loanHash;
        bytes     signature;
    }

    struct LoanRates {
        uint256 maxAmount;
        uint256 minAmount;
        uint256 minHeldToken;
        uint256 lenderFee;
        uint256 takerFee;
        uint32  interestRate;
        uint32  interestPeriod;
    }

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

    function storeNewPosition(
        MarginState.State storage state,
        bytes32 positionId,
        Position memory position,
        address loanPayer
    )
        internal
    {
        assert(!positionHasExisted(state, positionId));
        assert(position.owedToken != address(0));
        assert(position.heldToken != address(0));
        assert(position.owedToken != position.heldToken);
        assert(position.owner != address(0));
        assert(position.lender != address(0));
        assert(position.maxDuration != 0);
        assert(position.interestPeriod <= position.maxDuration);
        assert(position.callTimestamp == 0);
        assert(position.requiredDeposit == 0);

        state.positions[positionId].owedToken = position.owedToken;
        state.positions[positionId].heldToken = position.heldToken;
        state.positions[positionId].principal = position.principal;
        state.positions[positionId].callTimeLimit = position.callTimeLimit;
        state.positions[positionId].startTimestamp = TimestampHelper.getBlockTimestamp32();
        state.positions[positionId].maxDuration = position.maxDuration;
        state.positions[positionId].interestRate = position.interestRate;
        state.positions[positionId].interestPeriod = position.interestPeriod;

        state.positions[positionId].owner = TransferInternal.grantPositionOwnership(
            positionId,
            (position.owner != msg.sender) ? msg.sender : address(0),
            position.owner
        );

        state.positions[positionId].lender = TransferInternal.grantLoanOwnership(
            positionId,
            (position.lender != loanPayer) ? loanPayer : address(0),
            position.lender
        );
    }

    function getPositionIdFromNonce(
        uint256 nonce
    )
        internal
        view
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(msg.sender, nonce));
    }

    function getUnavailableLoanOfferingAmountImpl(
        MarginState.State storage state,
        bytes32 loanHash
    )
        internal
        view
        returns (uint256)
    {
        return state.loanFills[loanHash].add(state.loanCancels[loanHash]);
    }

    function cleanupPosition(
        MarginState.State storage state,
        bytes32 positionId
    )
        internal
    {
        delete state.positions[positionId];
        state.closedPositions[positionId] = true;
    }

    function calculateOwedAmount(
        Position storage position,
        uint256 closeAmount,
        uint256 endTimestamp
    )
        internal
        view
        returns (uint256)
    {
        uint256 timeElapsed = calculateEffectiveTimeElapsed(position, endTimestamp);

        return InterestImpl.getCompoundedInterest(
            closeAmount,
            position.interestRate,
            timeElapsed
        );
    }

    /**
     * Calculates time elapsed rounded up to the nearest interestPeriod
     */
    function calculateEffectiveTimeElapsed(
        Position storage position,
        uint256 timestamp
    )
        internal
        view
        returns (uint256)
    {
        uint256 elapsed = timestamp.sub(position.startTimestamp);

        // round up to interestPeriod
        uint256 period = position.interestPeriod;
        if (period > 1) {
            elapsed = MathHelpers.divisionRoundedUp(elapsed, period).mul(period);
        }

        // bound by maxDuration
        return Math.min256(
            elapsed,
            position.maxDuration
        );
    }

    function calculateLenderAmountForIncreasePosition(
        Position storage position,
        uint256 principalToAdd,
        uint256 endTimestamp
    )
        internal
        view
        returns (uint256)
    {
        uint256 timeElapsed = calculateEffectiveTimeElapsedForNewLender(position, endTimestamp);

        return InterestImpl.getCompoundedInterest(
            principalToAdd,
            position.interestRate,
            timeElapsed
        );
    }

    function getLoanOfferingHash(
        LoanOffering loanOffering
    )
        internal
        view
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked(
                address(this),
                loanOffering.owedToken,
                loanOffering.heldToken,
                loanOffering.payer,
                loanOffering.owner,
                loanOffering.taker,
                loanOffering.positionOwner,
                loanOffering.feeRecipient,
                loanOffering.lenderFeeToken,
                loanOffering.takerFeeToken,
                getValuesHash(loanOffering)
            )
        );
    }

    function getPositionBalanceImpl(
        MarginState.State storage state,
        bytes32 positionId
    )
        internal
        view
        returns(uint256)
    {
        return Vault(state.VAULT).balances(positionId, state.positions[positionId].heldToken);
    }

    function containsPositionImpl(
        MarginState.State storage state,
        bytes32 positionId
    )
        internal
        view
        returns (bool)
    {
        return state.positions[positionId].startTimestamp != 0;
    }

    function positionHasExisted(
        MarginState.State storage state,
        bytes32 positionId
    )
        internal
        view
        returns (bool)
    {
        return containsPositionImpl(state, positionId) || state.closedPositions[positionId];
    }

    function getPositionFromStorage(
        MarginState.State storage state,
        bytes32 positionId
    )
        internal
        view
        returns (Position storage)
    {
        Position storage position = state.positions[positionId];

        require(
            position.startTimestamp != 0,
            "MarginCommon#getPositionFromStorage: The position does not exist"
        );

        return position;
    }

    // ============ Private Helper-Functions ============

    /**
     * Calculates time elapsed rounded down to the nearest interestPeriod
     */
    function calculateEffectiveTimeElapsedForNewLender(
        Position storage position,
        uint256 timestamp
    )
        private
        view
        returns (uint256)
    {
        uint256 elapsed = timestamp.sub(position.startTimestamp);

        // round down to interestPeriod
        uint256 period = position.interestPeriod;
        if (period > 1) {
            elapsed = elapsed.div(period).mul(period);
        }

        // bound by maxDuration
        return Math.min256(
            elapsed,
            position.maxDuration
        );
    }

    function getValuesHash(
        LoanOffering loanOffering
    )
        private
        pure
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked(
                loanOffering.rates.maxAmount,
                loanOffering.rates.minAmount,
                loanOffering.rates.minHeldToken,
                loanOffering.rates.lenderFee,
                loanOffering.rates.takerFee,
                loanOffering.expirationTimestamp,
                loanOffering.salt,
                loanOffering.callTimeLimit,
                loanOffering.maxDuration,
                loanOffering.rates.interestRate,
                loanOffering.rates.interestPeriod
            )
        );
    }
}

// File: contracts/margin/interfaces/PayoutRecipient.sol

/**
 * @title PayoutRecipient
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to be the payoutRecipient in a
 * closePosition transaction.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface PayoutRecipient {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to receive payout from being the payoutRecipient
     * in a closePosition transaction. May redistribute any payout as necessary. Throws on error.
     *
     * @param  positionId         Unique ID of the position
     * @param  closeAmount        Amount of the position that was closed
     * @param  closer             Address of the account or contract that closed the position
     * @param  positionOwner      Address of the owner of the position
     * @param  heldToken          Address of the ERC20 heldToken
     * @param  payout             Number of tokens received from the payout
     * @param  totalHeldToken     Total amount of heldToken removed from vault during close
     * @param  payoutInHeldToken  True if payout is in heldToken, false if in owedToken
     * @return                    True if approved by the receiver
     */
    function receiveClosePositionPayout(
        bytes32 positionId,
        uint256 closeAmount,
        address closer,
        address positionOwner,
        address heldToken,
        uint256 payout,
        uint256 totalHeldToken,
        bool    payoutInHeldToken
    )
        external
        /* onlyMargin */
        returns (bool);
}

// File: contracts/margin/interfaces/lender/CloseLoanDelegator.sol

/**
 * @title CloseLoanDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses close a loan
 * owned by the smart contract.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface CloseLoanDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call
     * closeWithoutCounterparty().
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that (at most) the specified amount of the loan was
     * successfully closed.
     *
     * @param  closer           Address of the caller of closeWithoutCounterparty()
     * @param  payoutRecipient  Address of the recipient of tokens paid out from closing
     * @param  positionId       Unique ID of the position
     * @param  requestedAmount  Requested principal amount of the loan to close
     * @return                  1) This address to accept, a different address to ask that contract
     *                          2) The maximum amount that this contract is allowing
     */
    function closeLoanOnBehalfOf(
        address closer,
        address payoutRecipient,
        bytes32 positionId,
        uint256 requestedAmount
    )
        external
        /* onlyMargin */
        returns (address, uint256);
}

// File: contracts/margin/interfaces/owner/ClosePositionDelegator.sol

/**
 * @title ClosePositionDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses close a position
 * owned by the smart contract, allowing more complex logic to control positions.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface ClosePositionDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call closePosition().
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that (at-most) the specified amount of the position
     * was successfully closed.
     *
     * @param  closer           Address of the caller of the closePosition() function
     * @param  payoutRecipient  Address of the recipient of tokens paid out from closing
     * @param  positionId       Unique ID of the position
     * @param  requestedAmount  Requested principal amount of the position to close
     * @return                  1) This address to accept, a different address to ask that contract
     *                          2) The maximum amount that this contract is allowing
     */
    function closeOnBehalfOf(
        address closer,
        address payoutRecipient,
        bytes32 positionId,
        uint256 requestedAmount
    )
        external
        /* onlyMargin */
        returns (address, uint256);
}

// File: contracts/margin/impl/ClosePositionShared.sol

/**
 * @title ClosePositionShared
 * @author dYdX
 *
 * This library contains shared functionality between ClosePositionImpl and
 * CloseWithoutCounterpartyImpl
 */
library ClosePositionShared {
    using SafeMath for uint256;

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

    struct CloseTx {
        bytes32 positionId;
        uint256 originalPrincipal;
        uint256 closeAmount;
        uint256 owedTokenOwed;
        uint256 startingHeldTokenBalance;
        uint256 availableHeldToken;
        address payoutRecipient;
        address owedToken;
        address heldToken;
        address positionOwner;
        address positionLender;
        address exchangeWrapper;
        bool    payoutInHeldToken;
    }

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

    function closePositionStateUpdate(
        MarginState.State storage state,
        CloseTx memory transaction
    )
        internal
    {
        // Delete the position, or just decrease the principal
        if (transaction.closeAmount == transaction.originalPrincipal) {
            MarginCommon.cleanupPosition(state, transaction.positionId);
        } else {
            assert(
                transaction.originalPrincipal == state.positions[transaction.positionId].principal
            );
            state.positions[transaction.positionId].principal =
                transaction.originalPrincipal.sub(transaction.closeAmount);
        }
    }

    function sendTokensToPayoutRecipient(
        MarginState.State storage state,
        ClosePositionShared.CloseTx memory transaction,
        uint256 buybackCostInHeldToken,
        uint256 receivedOwedToken
    )
        internal
        returns (uint256)
    {
        uint256 payout;

        if (transaction.payoutInHeldToken) {
            // Send remaining heldToken to payoutRecipient
            payout = transaction.availableHeldToken.sub(buybackCostInHeldToken);

            Vault(state.VAULT).transferFromVault(
                transaction.positionId,
                transaction.heldToken,
                transaction.payoutRecipient,
                payout
            );
        } else {
            assert(transaction.exchangeWrapper != address(0));

            payout = receivedOwedToken.sub(transaction.owedTokenOwed);

            TokenProxy(state.TOKEN_PROXY).transferTokens(
                transaction.owedToken,
                transaction.exchangeWrapper,
                transaction.payoutRecipient,
                payout
            );
        }

        if (AddressUtils.isContract(transaction.payoutRecipient)) {
            require(
                PayoutRecipient(transaction.payoutRecipient).receiveClosePositionPayout(
                    transaction.positionId,
                    transaction.closeAmount,
                    msg.sender,
                    transaction.positionOwner,
                    transaction.heldToken,
                    payout,
                    transaction.availableHeldToken,
                    transaction.payoutInHeldToken
                ),
                "ClosePositionShared#sendTokensToPayoutRecipient: Payout recipient does not consent"
            );
        }

        // The ending heldToken balance of the vault should be the starting heldToken balance
        // minus the available heldToken amount
        assert(
            MarginCommon.getPositionBalanceImpl(state, transaction.positionId)
            == transaction.startingHeldTokenBalance.sub(transaction.availableHeldToken)
        );

        return payout;
    }

    function createCloseTx(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 requestedAmount,
        address payoutRecipient,
        address exchangeWrapper,
        bool payoutInHeldToken,
        bool isWithoutCounterparty
    )
        internal
        returns (CloseTx memory)
    {
        // Validate
        require(
            payoutRecipient != address(0),
            "ClosePositionShared#createCloseTx: Payout recipient cannot be 0"
        );
        require(
            requestedAmount > 0,
            "ClosePositionShared#createCloseTx: Requested close amount cannot be 0"
        );

        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        uint256 closeAmount = getApprovedAmount(
            position,
            positionId,
            requestedAmount,
            payoutRecipient,
            isWithoutCounterparty
        );

        return parseCloseTx(
            state,
            position,
            positionId,
            closeAmount,
            payoutRecipient,
            exchangeWrapper,
            payoutInHeldToken,
            isWithoutCounterparty
        );
    }

    // ============ Private Helper-Functions ============

    function getApprovedAmount(
        MarginCommon.Position storage position,
        bytes32 positionId,
        uint256 requestedAmount,
        address payoutRecipient,
        bool requireLenderApproval
    )
        private
        returns (uint256)
    {
        // Ensure enough principal
        uint256 allowedAmount = Math.min256(requestedAmount, position.principal);

        // Ensure owner consent
        allowedAmount = closePositionOnBehalfOfRecurse(
            position.owner,
            msg.sender,
            payoutRecipient,
            positionId,
            allowedAmount
        );

        // Ensure lender consent
        if (requireLenderApproval) {
            allowedAmount = closeLoanOnBehalfOfRecurse(
                position.lender,
                msg.sender,
                payoutRecipient,
                positionId,
                allowedAmount
            );
        }

        assert(allowedAmount > 0);
        assert(allowedAmount <= position.principal);
        assert(allowedAmount <= requestedAmount);

        return allowedAmount;
    }

    function closePositionOnBehalfOfRecurse(
        address contractAddr,
        address closer,
        address payoutRecipient,
        bytes32 positionId,
        uint256 closeAmount
    )
        private
        returns (uint256)
    {
        // no need to ask for permission
        if (closer == contractAddr) {
            return closeAmount;
        }

        (
            address newContractAddr,
            uint256 newCloseAmount
        ) = ClosePositionDelegator(contractAddr).closeOnBehalfOf(
            closer,
            payoutRecipient,
            positionId,
            closeAmount
        );

        require(
            newCloseAmount <= closeAmount,
            "ClosePositionShared#closePositionRecurse: newCloseAmount is greater than closeAmount"
        );
        require(
            newCloseAmount > 0,
            "ClosePositionShared#closePositionRecurse: newCloseAmount is zero"
        );

        if (newContractAddr != contractAddr) {
            closePositionOnBehalfOfRecurse(
                newContractAddr,
                closer,
                payoutRecipient,
                positionId,
                newCloseAmount
            );
        }

        return newCloseAmount;
    }

    function closeLoanOnBehalfOfRecurse(
        address contractAddr,
        address closer,
        address payoutRecipient,
        bytes32 positionId,
        uint256 closeAmount
    )
        private
        returns (uint256)
    {
        // no need to ask for permission
        if (closer == contractAddr) {
            return closeAmount;
        }

        (
            address newContractAddr,
            uint256 newCloseAmount
        ) = CloseLoanDelegator(contractAddr).closeLoanOnBehalfOf(
                closer,
                payoutRecipient,
                positionId,
                closeAmount
            );

        require(
            newCloseAmount <= closeAmount,
            "ClosePositionShared#closeLoanRecurse: newCloseAmount is greater than closeAmount"
        );
        require(
            newCloseAmount > 0,
            "ClosePositionShared#closeLoanRecurse: newCloseAmount is zero"
        );

        if (newContractAddr != contractAddr) {
            closeLoanOnBehalfOfRecurse(
                newContractAddr,
                closer,
                payoutRecipient,
                positionId,
                newCloseAmount
            );
        }

        return newCloseAmount;
    }

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

    function parseCloseTx(
        MarginState.State storage state,
        MarginCommon.Position storage position,
        bytes32 positionId,
        uint256 closeAmount,
        address payoutRecipient,
        address exchangeWrapper,
        bool payoutInHeldToken,
        bool isWithoutCounterparty
    )
        private
        view
        returns (CloseTx memory)
    {
        uint256 startingHeldTokenBalance = MarginCommon.getPositionBalanceImpl(state, positionId);

        uint256 availableHeldToken = MathHelpers.getPartialAmount(
            closeAmount,
            position.principal,
            startingHeldTokenBalance
        );
        uint256 owedTokenOwed = 0;

        if (!isWithoutCounterparty) {
            owedTokenOwed = MarginCommon.calculateOwedAmount(
                position,
                closeAmount,
                block.timestamp
            );
        }

        return CloseTx({
            positionId: positionId,
            originalPrincipal: position.principal,
            closeAmount: closeAmount,
            owedTokenOwed: owedTokenOwed,
            startingHeldTokenBalance: startingHeldTokenBalance,
            availableHeldToken: availableHeldToken,
            payoutRecipient: payoutRecipient,
            owedToken: position.owedToken,
            heldToken: position.heldToken,
            positionOwner: position.owner,
            positionLender: position.lender,
            exchangeWrapper: exchangeWrapper,
            payoutInHeldToken: payoutInHeldToken
        });
    }
}

// File: contracts/margin/interfaces/ExchangeWrapper.sol

/**
 * @title ExchangeWrapper
 * @author dYdX
 *
 * Contract interface that Exchange Wrapper smart contracts must implement in order to interface
 * with other smart contracts through a common interface.
 */
interface ExchangeWrapper {

    // ============ 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           Address of makerToken, the token to receive
     * @param  takerToken           Address of takerToken, the token to pay
     * @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 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         Address of makerToken, the token to receive
     * @param  takerToken         Address of takerToken, the token to pay
     * @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 transaction
     */
    function getExchangeCost(
        address makerToken,
        address takerToken,
        uint256 desiredMakerToken,
        bytes orderData
    )
        external
        view
        returns (uint256);
}

// File: contracts/margin/impl/ClosePositionImpl.sol

/**
 * @title ClosePositionImpl
 * @author dYdX
 *
 * This library contains the implementation for the closePosition function of Margin
 */
library ClosePositionImpl {
    using SafeMath for uint256;

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

    /**
     * A position was closed or partially closed
     */
    event PositionClosed(
        bytes32 indexed positionId,
        address indexed closer,
        address indexed payoutRecipient,
        uint256 closeAmount,
        uint256 remainingAmount,
        uint256 owedTokenPaidToLender,
        uint256 payoutAmount,
        uint256 buybackCostInHeldToken,
        bool    payoutInHeldToken
    );

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

    function closePositionImpl(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 requestedCloseAmount,
        address payoutRecipient,
        address exchangeWrapper,
        bool payoutInHeldToken,
        bytes memory orderData
    )
        public
        returns (uint256, uint256, uint256)
    {
        ClosePositionShared.CloseTx memory transaction = ClosePositionShared.createCloseTx(
            state,
            positionId,
            requestedCloseAmount,
            payoutRecipient,
            exchangeWrapper,
            payoutInHeldToken,
            false
        );

        (
            uint256 buybackCostInHeldToken,
            uint256 receivedOwedToken
        ) = returnOwedTokensToLender(
            state,
            transaction,
            orderData
        );

        uint256 payout = ClosePositionShared.sendTokensToPayoutRecipient(
            state,
            transaction,
            buybackCostInHeldToken,
            receivedOwedToken
        );

        ClosePositionShared.closePositionStateUpdate(state, transaction);

        logEventOnClose(
            transaction,
            buybackCostInHeldToken,
            payout
        );

        return (
            transaction.closeAmount,
            payout,
            transaction.owedTokenOwed
        );
    }

    // ============ Private Helper-Functions ============

    function returnOwedTokensToLender(
        MarginState.State storage state,
        ClosePositionShared.CloseTx memory transaction,
        bytes memory orderData
    )
        private
        returns (uint256, uint256)
    {
        uint256 buybackCostInHeldToken = 0;
        uint256 receivedOwedToken = 0;
        uint256 lenderOwedToken = transaction.owedTokenOwed;

        // Setting exchangeWrapper to 0x000... indicates owedToken should be taken directly
        // from msg.sender
        if (transaction.exchangeWrapper == address(0)) {
            require(
                transaction.payoutInHeldToken,
                "ClosePositionImpl#returnOwedTokensToLender: Cannot payout in owedToken"
            );

            // No DEX Order; send owedTokens directly from the closer to the lender
            TokenProxy(state.TOKEN_PROXY).transferTokens(
                transaction.owedToken,
                msg.sender,
                transaction.positionLender,
                lenderOwedToken
            );
        } else {
            // Buy back owedTokens using DEX Order and send to lender
            (buybackCostInHeldToken, receivedOwedToken) = buyBackOwedToken(
                state,
                transaction,
                orderData
            );

            // If no owedToken needed for payout: give lender all owedToken, even if more than owed
            if (transaction.payoutInHeldToken) {
                assert(receivedOwedToken >= lenderOwedToken);
                lenderOwedToken = receivedOwedToken;
            }

            // Transfer owedToken from the exchange wrapper to the lender
            TokenProxy(state.TOKEN_PROXY).transferTokens(
                transaction.owedToken,
                transaction.exchangeWrapper,
                transaction.positionLender,
                lenderOwedToken
            );
        }

        state.totalOwedTokenRepaidToLender[transaction.positionId] =
            state.totalOwedTokenRepaidToLender[transaction.positionId].add(lenderOwedToken);

        return (buybackCostInHeldToken, receivedOwedToken);
    }

    function buyBackOwedToken(
        MarginState.State storage state,
        ClosePositionShared.CloseTx transaction,
        bytes memory orderData
    )
        private
        returns (uint256, uint256)
    {
        // Ask the exchange wrapper the cost in heldToken to buy back the close
        // amount of owedToken
        uint256 buybackCostInHeldToken;

        if (transaction.payoutInHeldToken) {
            buybackCostInHeldToken = ExchangeWrapper(transaction.exchangeWrapper)
                .getExchangeCost(
                    transaction.owedToken,
                    transaction.heldToken,
                    transaction.owedTokenOwed,
                    orderData
                );

            // Require enough available heldToken to pay for the buyback
            require(
                buybackCostInHeldToken <= transaction.availableHeldToken,
                "ClosePositionImpl#buyBackOwedToken: Not enough available heldToken"
            );
        } else {
            buybackCostInHeldToken = transaction.availableHeldToken;
        }

        // Send the requisite heldToken to do the buyback from vault to exchange wrapper
        Vault(state.VAULT).transferFromVault(
            transaction.positionId,
            transaction.heldToken,
            transaction.exchangeWrapper,
            buybackCostInHeldToken
        );

        // Trade the heldToken for the owedToken
        uint256 receivedOwedToken = ExchangeWrapper(transaction.exchangeWrapper).exchange(
            msg.sender,
            state.TOKEN_PROXY,
            transaction.owedToken,
            transaction.heldToken,
            buybackCostInHeldToken,
            orderData
        );

        require(
            receivedOwedToken >= transaction.owedTokenOwed,
            "ClosePositionImpl#buyBackOwedToken: Did not receive enough owedToken"
        );

        return (buybackCostInHeldToken, receivedOwedToken);
    }

    function logEventOnClose(
        ClosePositionShared.CloseTx transaction,
        uint256 buybackCostInHeldToken,
        uint256 payout
    )
        private
    {
        emit PositionClosed(
            transaction.positionId,
            msg.sender,
            transaction.payoutRecipient,
            transaction.closeAmount,
            transaction.originalPrincipal.sub(transaction.closeAmount),
            transaction.owedTokenOwed,
            payout,
            buybackCostInHeldToken,
            transaction.payoutInHeldToken
        );
    }

}

// File: contracts/margin/impl/CloseWithoutCounterpartyImpl.sol

/**
 * @title CloseWithoutCounterpartyImpl
 * @author dYdX
 *
 * This library contains the implementation for the closeWithoutCounterpartyImpl function of
 * Margin
 */
library CloseWithoutCounterpartyImpl {
    using SafeMath for uint256;

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

    /**
     * A position was closed or partially closed
     */
    event PositionClosed(
        bytes32 indexed positionId,
        address indexed closer,
        address indexed payoutRecipient,
        uint256 closeAmount,
        uint256 remainingAmount,
        uint256 owedTokenPaidToLender,
        uint256 payoutAmount,
        uint256 buybackCostInHeldToken,
        bool payoutInHeldToken
    );

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

    function closeWithoutCounterpartyImpl(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 requestedCloseAmount,
        address payoutRecipient
    )
        public
        returns (uint256, uint256)
    {
        ClosePositionShared.CloseTx memory transaction = ClosePositionShared.createCloseTx(
            state,
            positionId,
            requestedCloseAmount,
            payoutRecipient,
            address(0),
            true,
            true
        );

        uint256 heldTokenPayout = ClosePositionShared.sendTokensToPayoutRecipient(
            state,
            transaction,
            0, // No buyback cost
            0  // Did not receive any owedToken
        );

        ClosePositionShared.closePositionStateUpdate(state, transaction);

        logEventOnCloseWithoutCounterparty(transaction);

        return (
            transaction.closeAmount,
            heldTokenPayout
        );
    }

    // ============ Private Helper-Functions ============

    function logEventOnCloseWithoutCounterparty(
        ClosePositionShared.CloseTx transaction
    )
        private
    {
        emit PositionClosed(
            transaction.positionId,
            msg.sender,
            transaction.payoutRecipient,
            transaction.closeAmount,
            transaction.originalPrincipal.sub(transaction.closeAmount),
            0,
            transaction.availableHeldToken,
            0,
            true
        );
    }
}

// File: contracts/margin/interfaces/owner/DepositCollateralDelegator.sol

/**
 * @title DepositCollateralDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses deposit heldTokens
 * into a position owned by the smart contract.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface DepositCollateralDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call depositCollateral().
     *
     * @param  depositor   Address of the caller of the depositCollateral() function
     * @param  positionId  Unique ID of the position
     * @param  amount      Requested deposit amount
     * @return             This address to accept, a different address to ask that contract
     */
    function depositCollateralOnBehalfOf(
        address depositor,
        bytes32 positionId,
        uint256 amount
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/DepositCollateralImpl.sol

/**
 * @title DepositCollateralImpl
 * @author dYdX
 *
 * This library contains the implementation for the deposit function of Margin
 */
library DepositCollateralImpl {
    using SafeMath for uint256;

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

    /**
     * Additional collateral for a position was posted by the owner
     */
    event AdditionalCollateralDeposited(
        bytes32 indexed positionId,
        uint256 amount,
        address depositor
    );

    /**
     * A margin call was canceled
     */
    event MarginCallCanceled(
        bytes32 indexed positionId,
        address indexed lender,
        address indexed owner,
        uint256 depositAmount
    );

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

    function depositCollateralImpl(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 depositAmount
    )
        public
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        require(
            depositAmount > 0,
            "DepositCollateralImpl#depositCollateralImpl: Deposit amount cannot be 0"
        );

        // Ensure owner consent
        depositCollateralOnBehalfOfRecurse(
            position.owner,
            msg.sender,
            positionId,
            depositAmount
        );

        Vault(state.VAULT).transferToVault(
            positionId,
            position.heldToken,
            msg.sender,
            depositAmount
        );

        // cancel margin call if applicable
        bool marginCallCanceled = false;
        uint256 requiredDeposit = position.requiredDeposit;
        if (position.callTimestamp > 0 && requiredDeposit > 0) {
            if (depositAmount >= requiredDeposit) {
                position.requiredDeposit = 0;
                position.callTimestamp = 0;
                marginCallCanceled = true;
            } else {
                position.requiredDeposit = position.requiredDeposit.sub(depositAmount);
            }
        }

        emit AdditionalCollateralDeposited(
            positionId,
            depositAmount,
            msg.sender
        );

        if (marginCallCanceled) {
            emit MarginCallCanceled(
                positionId,
                position.lender,
                msg.sender,
                depositAmount
            );
        }
    }

    // ============ Private Helper-Functions ============

    function depositCollateralOnBehalfOfRecurse(
        address contractAddr,
        address depositor,
        bytes32 positionId,
        uint256 amount
    )
        private
    {
        // no need to ask for permission
        if (depositor == contractAddr) {
            return;
        }

        address newContractAddr =
            DepositCollateralDelegator(contractAddr).depositCollateralOnBehalfOf(
                depositor,
                positionId,
                amount
            );

        // if not equal, recurse
        if (newContractAddr != contractAddr) {
            depositCollateralOnBehalfOfRecurse(
                newContractAddr,
                depositor,
                positionId,
                amount
            );
        }
    }
}

// File: contracts/margin/interfaces/lender/ForceRecoverCollateralDelegator.sol

/**
 * @title ForceRecoverCollateralDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses
 * forceRecoverCollateral() a loan owned by the smart contract.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface ForceRecoverCollateralDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call
     * forceRecoverCollateral().
     *
     * NOTE: If not returning zero address (or not reverting), this contract must assume that Margin
     * will either revert the entire transaction or that the collateral was forcibly recovered.
     *
     * @param  recoverer   Address of the caller of the forceRecoverCollateral() function
     * @param  positionId  Unique ID of the position
     * @param  recipient   Address to send the recovered tokens to
     * @return             This address to accept, a different address to ask that contract
     */
    function forceRecoverCollateralOnBehalfOf(
        address recoverer,
        bytes32 positionId,
        address recipient
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/ForceRecoverCollateralImpl.sol

/* solium-disable-next-line max-len*/

/**
 * @title ForceRecoverCollateralImpl
 * @author dYdX
 *
 * This library contains the implementation for the forceRecoverCollateral function of Margin
 */
library ForceRecoverCollateralImpl {
    using SafeMath for uint256;

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

    /**
     * Collateral for a position was forcibly recovered
     */
    event CollateralForceRecovered(
        bytes32 indexed positionId,
        address indexed recipient,
        uint256 amount
    );

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

    function forceRecoverCollateralImpl(
        MarginState.State storage state,
        bytes32 positionId,
        address recipient
    )
        public
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        // Can only force recover after either:
        // 1) The loan was called and the call period has elapsed
        // 2) The maxDuration of the position has elapsed
        require( /* solium-disable-next-line */
            (
                position.callTimestamp > 0
                && block.timestamp >= uint256(position.callTimestamp).add(position.callTimeLimit)
            ) || (
                block.timestamp >= uint256(position.startTimestamp).add(position.maxDuration)
            ),
            "ForceRecoverCollateralImpl#forceRecoverCollateralImpl: Cannot recover yet"
        );

        // Ensure lender consent
        forceRecoverCollateralOnBehalfOfRecurse(
            position.lender,
            msg.sender,
            positionId,
            recipient
        );

        // Send the tokens
        uint256 heldTokenRecovered = MarginCommon.getPositionBalanceImpl(state, positionId);
        Vault(state.VAULT).transferFromVault(
            positionId,
            position.heldToken,
            recipient,
            heldTokenRecovered
        );

        // Delete the position
        // NOTE: Since position is a storage pointer, this will also set all fields on
        //       the position variable to 0
        MarginCommon.cleanupPosition(
            state,
            positionId
        );

        // Log an event
        emit CollateralForceRecovered(
            positionId,
            recipient,
            heldTokenRecovered
        );

        return heldTokenRecovered;
    }

    // ============ Private Helper-Functions ============

    function forceRecoverCollateralOnBehalfOfRecurse(
        address contractAddr,
        address recoverer,
        bytes32 positionId,
        address recipient
    )
        private
    {
        // no need to ask for permission
        if (recoverer == contractAddr) {
            return;
        }

        address newContractAddr =
            ForceRecoverCollateralDelegator(contractAddr).forceRecoverCollateralOnBehalfOf(
                recoverer,
                positionId,
                recipient
            );

        if (newContractAddr != contractAddr) {
            forceRecoverCollateralOnBehalfOfRecurse(
                newContractAddr,
                recoverer,
                positionId,
                recipient
            );
        }
    }
}

// File: contracts/lib/TypedSignature.sol

/**
 * @title TypedSignature
 * @author dYdX
 *
 * Allows for ecrecovery of signed hashes with three different prepended messages:
 * 1) ""
 * 2) "\x19Ethereum Signed Message:\n32"
 * 3) "\x19Ethereum Signed Message:\n\x20"
 */
library TypedSignature {

    // Solidity does not offer guarantees about enum values, so we define them explicitly
    uint8 private constant SIGTYPE_INVALID = 0;
    uint8 private constant SIGTYPE_ECRECOVER_DEC = 1;
    uint8 private constant SIGTYPE_ECRECOVER_HEX = 2;
    uint8 private constant SIGTYPE_UNSUPPORTED = 3;

    // prepended message with the length of the signed hash in hexadecimal
    bytes constant private PREPEND_HEX = "\x19Ethereum Signed Message:\n\x20";

    // prepended message with the length of the signed hash in decimal
    bytes constant private PREPEND_DEC = "\x19Ethereum Signed Message:\n32";

    /**
     * Gives the address of the signer of a hash. Allows for three common prepended strings.
     *
     * @param  hash               Hash that was signed (does not include prepended message)
     * @param  signatureWithType  Type and ECDSA signature with structure: {1:type}{1:v}{32:r}{32:s}
     * @return                    address of the signer of the hash
     */
    function recover(
        bytes32 hash,
        bytes signatureWithType
    )
        internal
        pure
        returns (address)
    {
        require(
            signatureWithType.length == 66,
            "SignatureValidator#validateSignature: invalid signature length"
        );

        uint8 sigType = uint8(signatureWithType[0]);

        require(
            sigType > uint8(SIGTYPE_INVALID),
            "SignatureValidator#validateSignature: invalid signature type"
        );
        require(
            sigType < uint8(SIGTYPE_UNSUPPORTED),
            "SignatureValidator#validateSignature: unsupported signature type"
        );

        uint8 v = uint8(signatureWithType[1]);
        bytes32 r;
        bytes32 s;

        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            r := mload(add(signatureWithType, 34))
            s := mload(add(signatureWithType, 66))
        }

        bytes32 signedHash;
        if (sigType == SIGTYPE_ECRECOVER_DEC) {
            signedHash = keccak256(abi.encodePacked(PREPEND_DEC, hash));
        } else {
            assert(sigType == SIGTYPE_ECRECOVER_HEX);
            signedHash = keccak256(abi.encodePacked(PREPEND_HEX, hash));
        }

        return ecrecover(
            signedHash,
            v,
            r,
            s
        );
    }
}

// File: contracts/margin/interfaces/LoanOfferingVerifier.sol

/**
 * @title LoanOfferingVerifier
 * @author dYdX
 *
 * Interface that smart contracts must implement to be able to make off-chain generated
 * loan offerings.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface LoanOfferingVerifier {

    /**
     * Function a smart contract must implement to be able to consent to a loan. The loan offering
     * will be generated off-chain. The "loan owner" address will own the loan-side of the resulting
     * position.
     *
     * If true is returned, and no errors are thrown by the Margin contract, the loan will have
     * occurred. This means that verifyLoanOffering can also be used to update internal contract
     * state on a loan.
     *
     * @param  addresses    Array of addresses:
     *
     *  [0] = owedToken
     *  [1] = heldToken
     *  [2] = loan payer
     *  [3] = loan owner
     *  [4] = loan taker
     *  [5] = loan positionOwner
     *  [6] = loan fee recipient
     *  [7] = loan lender fee token
     *  [8] = loan taker fee token
     *
     * @param  values256    Values corresponding to:
     *
     *  [0] = loan maximum amount
     *  [1] = loan minimum amount
     *  [2] = loan minimum heldToken
     *  [3] = loan lender fee
     *  [4] = loan taker fee
     *  [5] = loan expiration timestamp (in seconds)
     *  [6] = loan salt
     *
     * @param  values32     Values corresponding to:
     *
     *  [0] = loan call time limit (in seconds)
     *  [1] = loan maxDuration (in seconds)
     *  [2] = loan interest rate (annual nominal percentage times 10**6)
     *  [3] = loan interest update period (in seconds)
     *
     * @param  positionId   Unique ID of the position
     * @param  signature    Arbitrary bytes; may or may not be an ECDSA signature
     * @return              This address to accept, a different address to ask that contract
     */
    function verifyLoanOffering(
        address[9] addresses,
        uint256[7] values256,
        uint32[4] values32,
        bytes32 positionId,
        bytes signature
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/BorrowShared.sol

/**
 * @title BorrowShared
 * @author dYdX
 *
 * This library contains shared functionality between OpenPositionImpl and IncreasePositionImpl.
 * Both use a Loan Offering and a DEX Order to open or increase a position.
 */
library BorrowShared {
    using SafeMath for uint256;

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

    struct Tx {
        bytes32 positionId;
        address owner;
        uint256 principal;
        uint256 lenderAmount;
        MarginCommon.LoanOffering loanOffering;
        address exchangeWrapper;
        bool depositInHeldToken;
        uint256 depositAmount;
        uint256 collateralAmount;
        uint256 heldTokenFromSell;
    }

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

    /**
     * Validate the transaction before exchanging heldToken for owedToken
     */
    function validateTxPreSell(
        MarginState.State storage state,
        Tx memory transaction
    )
        internal
    {
        assert(transaction.lenderAmount >= transaction.principal);

        require(
            transaction.principal > 0,
            "BorrowShared#validateTxPreSell: Positions with 0 principal are not allowed"
        );

        // If the taker is 0x0 then any address can take it. Otherwise only the taker can use it.
        if (transaction.loanOffering.taker != address(0)) {
            require(
                msg.sender == transaction.loanOffering.taker,
                "BorrowShared#validateTxPreSell: Invalid loan offering taker"
            );
        }

        // If the positionOwner is 0x0 then any address can be set as the position owner.
        // Otherwise only the specified positionOwner can be set as the position owner.
        if (transaction.loanOffering.positionOwner != address(0)) {
            require(
                transaction.owner == transaction.loanOffering.positionOwner,
                "BorrowShared#validateTxPreSell: Invalid position owner"
            );
        }

        // Require the loan offering to be approved by the payer
        if (AddressUtils.isContract(transaction.loanOffering.payer)) {
            getConsentFromSmartContractLender(transaction);
        } else {
            require(
                transaction.loanOffering.payer == TypedSignature.recover(
                    transaction.loanOffering.loanHash,
                    transaction.loanOffering.signature
                ),
                "BorrowShared#validateTxPreSell: Invalid loan offering signature"
            );
        }

        // Validate the amount is <= than max and >= min
        uint256 unavailable = MarginCommon.getUnavailableLoanOfferingAmountImpl(
            state,
            transaction.loanOffering.loanHash
        );
        require(
            transaction.lenderAmount.add(unavailable) <= transaction.loanOffering.rates.maxAmount,
            "BorrowShared#validateTxPreSell: Loan offering does not have enough available"
        );

        require(
            transaction.lenderAmount >= transaction.loanOffering.rates.minAmount,
            "BorrowShared#validateTxPreSell: Lender amount is below loan offering minimum amount"
        );

        require(
            transaction.loanOffering.owedToken != transaction.loanOffering.heldToken,
            "BorrowShared#validateTxPreSell: owedToken cannot be equal to heldToken"
        );

        require(
            transaction.owner != address(0),
            "BorrowShared#validateTxPreSell: Position owner cannot be 0"
        );

        require(
            transaction.loanOffering.owner != address(0),
            "BorrowShared#validateTxPreSell: Loan owner cannot be 0"
        );

        require(
            transaction.loanOffering.expirationTimestamp > block.timestamp,
            "BorrowShared#validateTxPreSell: Loan offering is expired"
        );

        require(
            transaction.loanOffering.maxDuration > 0,
            "BorrowShared#validateTxPreSell: Loan offering has 0 maximum duration"
        );

        require(
            transaction.loanOffering.rates.interestPeriod <= transaction.loanOffering.maxDuration,
            "BorrowShared#validateTxPreSell: Loan offering interestPeriod > maxDuration"
        );

        // The minimum heldToken is validated after executing the sell
        // Position and loan ownership is validated in TransferInternal
    }

    /**
     * Validate the transaction after exchanging heldToken for owedToken, pay out fees, and store
     * how much of the loan was used.
     */
    function doPostSell(
        MarginState.State storage state,
        Tx memory transaction
    )
        internal
    {
        validateTxPostSell(transaction);

        // Transfer feeTokens from trader and lender
        transferLoanFees(state, transaction);

        // Update global amounts for the loan
        state.loanFills[transaction.loanOffering.loanHash] =
            state.loanFills[transaction.loanOffering.loanHash].add(transaction.lenderAmount);
    }

    /**
     * Sells the owedToken from the lender (and from the deposit if in owedToken) using the
     * exchangeWrapper, then puts the resulting heldToken into the vault. Only trades for
     * maxHeldTokenToBuy of heldTokens at most.
     */
    function doSell(
        MarginState.State storage state,
        Tx transaction,
        bytes orderData,
        uint256 maxHeldTokenToBuy
    )
        internal
        returns (uint256)
    {
        // Move owedTokens from lender to exchange wrapper
        pullOwedTokensFromLender(state, transaction);

        // Sell just the lender's owedToken (if trader deposit is in heldToken)
        // Otherwise sell both the lender's owedToken and the trader's deposit in owedToken
        uint256 sellAmount = transaction.depositInHeldToken ?
            transaction.lenderAmount :
            transaction.lenderAmount.add(transaction.depositAmount);

        // Do the trade, taking only the maxHeldTokenToBuy if more is returned
        uint256 heldTokenFromSell = Math.min256(
            maxHeldTokenToBuy,
            ExchangeWrapper(transaction.exchangeWrapper).exchange(
                msg.sender,
                state.TOKEN_PROXY,
                transaction.loanOffering.heldToken,
                transaction.loanOffering.owedToken,
                sellAmount,
                orderData
            )
        );

        // Move the tokens to the vault
        Vault(state.VAULT).transferToVault(
            transaction.positionId,
            transaction.loanOffering.heldToken,
            transaction.exchangeWrapper,
            heldTokenFromSell
        );

        // Update collateral amount
        transaction.collateralAmount = transaction.collateralAmount.add(heldTokenFromSell);

        return heldTokenFromSell;
    }

    /**
     * Take the owedToken deposit from the trader and give it to the exchange wrapper so that it can
     * be sold for heldToken.
     */
    function doDepositOwedToken(
        MarginState.State storage state,
        Tx transaction
    )
        internal
    {
        TokenProxy(state.TOKEN_PROXY).transferTokens(
            transaction.loanOffering.owedToken,
            msg.sender,
            transaction.exchangeWrapper,
            transaction.depositAmount
        );
    }

    /**
     * Take the heldToken deposit from the trader and move it to the vault.
     */
    function doDepositHeldToken(
        MarginState.State storage state,
        Tx transaction
    )
        internal
    {
        Vault(state.VAULT).transferToVault(
            transaction.positionId,
            transaction.loanOffering.heldToken,
            msg.sender,
            transaction.depositAmount
        );

        // Update collateral amount
        transaction.collateralAmount = transaction.collateralAmount.add(transaction.depositAmount);
    }

    // ============ Private Helper-Functions ============

    function validateTxPostSell(
        Tx transaction
    )
        private
        pure
    {
        uint256 expectedCollateral = transaction.depositInHeldToken ?
            transaction.heldTokenFromSell.add(transaction.depositAmount) :
            transaction.heldTokenFromSell;
        assert(transaction.collateralAmount == expectedCollateral);

        uint256 loanOfferingMinimumHeldToken = MathHelpers.getPartialAmountRoundedUp(
            transaction.lenderAmount,
            transaction.loanOffering.rates.maxAmount,
            transaction.loanOffering.rates.minHeldToken
        );
        require(
            transaction.collateralAmount >= loanOfferingMinimumHeldToken,
            "BorrowShared#validateTxPostSell: Loan offering minimum held token not met"
        );
    }

    function getConsentFromSmartContractLender(
        Tx transaction
    )
        private
    {
        verifyLoanOfferingRecurse(
            transaction.loanOffering.payer,
            getLoanOfferingAddresses(transaction),
            getLoanOfferingValues256(transaction),
            getLoanOfferingValues32(transaction),
            transaction.positionId,
            transaction.loanOffering.signature
        );
    }

    function verifyLoanOfferingRecurse(
        address contractAddr,
        address[9] addresses,
        uint256[7] values256,
        uint32[4] values32,
        bytes32 positionId,
        bytes signature
    )
        private
    {
        address newContractAddr = LoanOfferingVerifier(contractAddr).verifyLoanOffering(
            addresses,
            values256,
            values32,
            positionId,
            signature
        );

        if (newContractAddr != contractAddr) {
            verifyLoanOfferingRecurse(
                newContractAddr,
                addresses,
                values256,
                values32,
                positionId,
                signature
            );
        }
    }

    function pullOwedTokensFromLender(
        MarginState.State storage state,
        Tx transaction
    )
        private
    {
        // Transfer owedToken to the exchange wrapper
        TokenProxy(state.TOKEN_PROXY).transferTokens(
            transaction.loanOffering.owedToken,
            transaction.loanOffering.payer,
            transaction.exchangeWrapper,
            transaction.lenderAmount
        );
    }

    function transferLoanFees(
        MarginState.State storage state,
        Tx transaction
    )
        private
    {
        // 0 fee address indicates no fees
        if (transaction.loanOffering.feeRecipient == address(0)) {
            return;
        }

        TokenProxy proxy = TokenProxy(state.TOKEN_PROXY);

        uint256 lenderFee = MathHelpers.getPartialAmount(
            transaction.lenderAmount,
            transaction.loanOffering.rates.maxAmount,
            transaction.loanOffering.rates.lenderFee
        );
        uint256 takerFee = MathHelpers.getPartialAmount(
            transaction.lenderAmount,
            transaction.loanOffering.rates.maxAmount,
            transaction.loanOffering.rates.takerFee
        );

        if (lenderFee > 0) {
            proxy.transferTokens(
                transaction.loanOffering.lenderFeeToken,
                transaction.loanOffering.payer,
                transaction.loanOffering.feeRecipient,
                lenderFee
            );
        }

        if (takerFee > 0) {
            proxy.transferTokens(
                transaction.loanOffering.takerFeeToken,
                msg.sender,
                transaction.loanOffering.feeRecipient,
                takerFee
            );
        }
    }

    function getLoanOfferingAddresses(
        Tx transaction
    )
        private
        pure
        returns (address[9])
    {
        return [
            transaction.loanOffering.owedToken,
            transaction.loanOffering.heldToken,
            transaction.loanOffering.payer,
            transaction.loanOffering.owner,
            transaction.loanOffering.taker,
            transaction.loanOffering.positionOwner,
            transaction.loanOffering.feeRecipient,
            transaction.loanOffering.lenderFeeToken,
            transaction.loanOffering.takerFeeToken
        ];
    }

    function getLoanOfferingValues256(
        Tx transaction
    )
        private
        pure
        returns (uint256[7])
    {
        return [
            transaction.loanOffering.rates.maxAmount,
            transaction.loanOffering.rates.minAmount,
            transaction.loanOffering.rates.minHeldToken,
            transaction.loanOffering.rates.lenderFee,
            transaction.loanOffering.rates.takerFee,
            transaction.loanOffering.expirationTimestamp,
            transaction.loanOffering.salt
        ];
    }

    function getLoanOfferingValues32(
        Tx transaction
    )
        private
        pure
        returns (uint32[4])
    {
        return [
            transaction.loanOffering.callTimeLimit,
            transaction.loanOffering.maxDuration,
            transaction.loanOffering.rates.interestRate,
            transaction.loanOffering.rates.interestPeriod
        ];
    }
}

// File: contracts/margin/interfaces/lender/IncreaseLoanDelegator.sol

/**
 * @title IncreaseLoanDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to own loans on behalf of other accounts.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface IncreaseLoanDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to allow additional value to be added onto
     * an owned loan. Margin will call this on the owner of a loan during increasePosition().
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that the loan size was successfully increased.
     *
     * @param  payer           Lender adding additional funds to the position
     * @param  positionId      Unique ID of the position
     * @param  principalAdded  Principal amount to be added to the position
     * @param  lentAmount      Amount of owedToken lent by the lender (principal plus interest, or
     *                         zero if increaseWithoutCounterparty() is used).
     * @return                 This address to accept, a different address to ask that contract
     */
    function increaseLoanOnBehalfOf(
        address payer,
        bytes32 positionId,
        uint256 principalAdded,
        uint256 lentAmount
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/interfaces/owner/IncreasePositionDelegator.sol

/**
 * @title IncreasePositionDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to own position on behalf of other
 * accounts
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface IncreasePositionDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to allow additional value to be added onto
     * an owned position. Margin will call this on the owner of a position during increasePosition()
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that the position size was successfully increased.
     *
     * @param  trader          Address initiating the addition of funds to the position
     * @param  positionId      Unique ID of the position
     * @param  principalAdded  Amount of principal to be added to the position
     * @return                 This address to accept, a different address to ask that contract
     */
    function increasePositionOnBehalfOf(
        address trader,
        bytes32 positionId,
        uint256 principalAdded
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/IncreasePositionImpl.sol

/**
 * @title IncreasePositionImpl
 * @author dYdX
 *
 * This library contains the implementation for the increasePosition function of Margin
 */
library IncreasePositionImpl {
    using SafeMath for uint256;

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

    /*
     * A position was increased
     */
    event PositionIncreased(
        bytes32 indexed positionId,
        address indexed trader,
        address indexed lender,
        address positionOwner,
        address loanOwner,
        bytes32 loanHash,
        address loanFeeRecipient,
        uint256 amountBorrowed,
        uint256 principalAdded,
        uint256 heldTokenFromSell,
        uint256 depositAmount,
        bool    depositInHeldToken
    );

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

    function increasePositionImpl(
        MarginState.State storage state,
        bytes32 positionId,
        address[7] addresses,
        uint256[8] values256,
        uint32[2] values32,
        bool depositInHeldToken,
        bytes signature,
        bytes orderData
    )
        public
        returns (uint256)
    {
        // Also ensures that the position exists
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        BorrowShared.Tx memory transaction = parseIncreasePositionTx(
            position,
            positionId,
            addresses,
            values256,
            values32,
            depositInHeldToken,
            signature
        );

        validateIncrease(state, transaction, position);

        doBorrowAndSell(state, transaction, orderData);

        updateState(
            position,
            transaction.positionId,
            transaction.principal,
            transaction.lenderAmount,
            transaction.loanOffering.payer
        );

        // LOG EVENT
        recordPositionIncreased(transaction, position);

        return transaction.lenderAmount;
    }

    function increaseWithoutCounterpartyImpl(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 principalToAdd
    )
        public
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        // Disallow adding 0 principal
        require(
            principalToAdd > 0,
            "IncreasePositionImpl#increaseWithoutCounterpartyImpl: Cannot add 0 principal"
        );

        // Disallow additions after maximum duration
        require(
            block.timestamp < uint256(position.startTimestamp).add(position.maxDuration),
            "IncreasePositionImpl#increaseWithoutCounterpartyImpl: Cannot increase after maxDuration"
        );

        uint256 heldTokenAmount = getCollateralNeededForAddedPrincipal(
            state,
            position,
            positionId,
            principalToAdd
        );

        Vault(state.VAULT).transferToVault(
            positionId,
            position.heldToken,
            msg.sender,
            heldTokenAmount
        );

        updateState(
            position,
            positionId,
            principalToAdd,
            0, // lent amount
            msg.sender
        );

        emit PositionIncreased(
            positionId,
            msg.sender,
            msg.sender,
            position.owner,
            position.lender,
            "",
            address(0),
            0,
            principalToAdd,
            0,
            heldTokenAmount,
            true
        );

        return heldTokenAmount;
    }

    // ============ Private Helper-Functions ============

    function doBorrowAndSell(
        MarginState.State storage state,
        BorrowShared.Tx memory transaction,
        bytes orderData
    )
        private
    {
        // Calculate the number of heldTokens to add
        uint256 collateralToAdd = getCollateralNeededForAddedPrincipal(
            state,
            state.positions[transaction.positionId],
            transaction.positionId,
            transaction.principal
        );

        // Do pre-exchange validations
        BorrowShared.validateTxPreSell(state, transaction);

        // Calculate and deposit owedToken
        uint256 maxHeldTokenFromSell = MathHelpers.maxUint256();
        if (!transaction.depositInHeldToken) {
            transaction.depositAmount =
                getOwedTokenDeposit(transaction, collateralToAdd, orderData);
            BorrowShared.doDepositOwedToken(state, transaction);
            maxHeldTokenFromSell = collateralToAdd;
        }

        // Sell owedToken for heldToken using the exchange wrapper
        transaction.heldTokenFromSell = BorrowShared.doSell(
            state,
            transaction,
            orderData,
            maxHeldTokenFromSell
        );

        // Calculate and deposit heldToken
        if (transaction.depositInHeldToken) {
            require(
                transaction.heldTokenFromSell <= collateralToAdd,
                "IncreasePositionImpl#doBorrowAndSell: DEX order gives too much heldToken"
            );
            transaction.depositAmount = collateralToAdd.sub(transaction.heldTokenFromSell);
            BorrowShared.doDepositHeldToken(state, transaction);
        }

        // Make sure the actual added collateral is what is expected
        assert(transaction.collateralAmount == collateralToAdd);

        // Do post-exchange validations
        BorrowShared.doPostSell(state, transaction);
    }

    function getOwedTokenDeposit(
        BorrowShared.Tx transaction,
        uint256 collateralToAdd,
        bytes orderData
    )
        private
        view
        returns (uint256)
    {
        uint256 totalOwedToken = ExchangeWrapper(transaction.exchangeWrapper).getExchangeCost(
            transaction.loanOffering.heldToken,
            transaction.loanOffering.owedToken,
            collateralToAdd,
            orderData
        );

        require(
            transaction.lenderAmount <= totalOwedToken,
            "IncreasePositionImpl#getOwedTokenDeposit: Lender amount is more than required"
        );

        return totalOwedToken.sub(transaction.lenderAmount);
    }

    function validateIncrease(
        MarginState.State storage state,
        BorrowShared.Tx transaction,
        MarginCommon.Position storage position
    )
        private
        view
    {
        assert(MarginCommon.containsPositionImpl(state, transaction.positionId));

        require(
            position.callTimeLimit <= transaction.loanOffering.callTimeLimit,
            "IncreasePositionImpl#validateIncrease: Loan callTimeLimit is less than the position"
        );

        // require the position to end no later than the loanOffering's maximum acceptable end time
        uint256 positionEndTimestamp = uint256(position.startTimestamp).add(position.maxDuration);
        uint256 offeringEndTimestamp = block.timestamp.add(transaction.loanOffering.maxDuration);
        require(
            positionEndTimestamp <= offeringEndTimestamp,
            "IncreasePositionImpl#validateIncrease: Loan end timestamp is less than the position"
        );

        require(
            block.timestamp < positionEndTimestamp,
            "IncreasePositionImpl#validateIncrease: Position has passed its maximum duration"
        );
    }

    function getCollateralNeededForAddedPrincipal(
        MarginState.State storage state,
        MarginCommon.Position storage position,
        bytes32 positionId,
        uint256 principalToAdd
    )
        private
        view
        returns (uint256)
    {
        uint256 heldTokenBalance = MarginCommon.getPositionBalanceImpl(state, positionId);

        return MathHelpers.getPartialAmountRoundedUp(
            principalToAdd,
            position.principal,
            heldTokenBalance
        );
    }

    function updateState(
        MarginCommon.Position storage position,
        bytes32 positionId,
        uint256 principalAdded,
        uint256 owedTokenLent,
        address loanPayer
    )
        private
    {
        position.principal = position.principal.add(principalAdded);

        address owner = position.owner;
        address lender = position.lender;

        // Ensure owner consent
        increasePositionOnBehalfOfRecurse(
            owner,
            msg.sender,
            positionId,
            principalAdded
        );

        // Ensure lender consent
        increaseLoanOnBehalfOfRecurse(
            lender,
            loanPayer,
            positionId,
            principalAdded,
            owedTokenLent
        );
    }

    function increasePositionOnBehalfOfRecurse(
        address contractAddr,
        address trader,
        bytes32 positionId,
        uint256 principalAdded
    )
        private
    {
        // Assume owner approval if not a smart contract and they increased their own position
        if (trader == contractAddr && !AddressUtils.isContract(contractAddr)) {
            return;
        }

        address newContractAddr =
            IncreasePositionDelegator(contractAddr).increasePositionOnBehalfOf(
                trader,
                positionId,
                principalAdded
            );

        if (newContractAddr != contractAddr) {
            increasePositionOnBehalfOfRecurse(
                newContractAddr,
                trader,
                positionId,
                principalAdded
            );
        }
    }

    function increaseLoanOnBehalfOfRecurse(
        address contractAddr,
        address payer,
        bytes32 positionId,
        uint256 principalAdded,
        uint256 amountLent
    )
        private
    {
        // Assume lender approval if not a smart contract and they increased their own loan
        if (payer == contractAddr && !AddressUtils.isContract(contractAddr)) {
            return;
        }

        address newContractAddr =
            IncreaseLoanDelegator(contractAddr).increaseLoanOnBehalfOf(
                payer,
                positionId,
                principalAdded,
                amountLent
            );

        if (newContractAddr != contractAddr) {
            increaseLoanOnBehalfOfRecurse(
                newContractAddr,
                payer,
                positionId,
                principalAdded,
                amountLent
            );
        }
    }

    function recordPositionIncreased(
        BorrowShared.Tx transaction,
        MarginCommon.Position storage position
    )
        private
    {
        emit PositionIncreased(
            transaction.positionId,
            msg.sender,
            transaction.loanOffering.payer,
            position.owner,
            position.lender,
            transaction.loanOffering.loanHash,
            transaction.loanOffering.feeRecipient,
            transaction.lenderAmount,
            transaction.principal,
            transaction.heldTokenFromSell,
            transaction.depositAmount,
            transaction.depositInHeldToken
        );
    }

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

    function parseIncreasePositionTx(
        MarginCommon.Position storage position,
        bytes32 positionId,
        address[7] addresses,
        uint256[8] values256,
        uint32[2] values32,
        bool depositInHeldToken,
        bytes signature
    )
        private
        view
        returns (BorrowShared.Tx memory)
    {
        uint256 principal = values256[7];

        uint256 lenderAmount = MarginCommon.calculateLenderAmountForIncreasePosition(
            position,
            principal,
            block.timestamp
        );
        assert(lenderAmount >= principal);

        BorrowShared.Tx memory transaction = BorrowShared.Tx({
            positionId: positionId,
            owner: position.owner,
            principal: principal,
            lenderAmount: lenderAmount,
            loanOffering: parseLoanOfferingFromIncreasePositionTx(
                position,
                addresses,
                values256,
                values32,
                signature
            ),
            exchangeWrapper: addresses[6],
            depositInHeldToken: depositInHeldToken,
            depositAmount: 0, // set later
            collateralAmount: 0, // set later
            heldTokenFromSell: 0 // set later
        });

        return transaction;
    }

    function parseLoanOfferingFromIncreasePositionTx(
        MarginCommon.Position storage position,
        address[7] addresses,
        uint256[8] values256,
        uint32[2] values32,
        bytes signature
    )
        private
        view
        returns (MarginCommon.LoanOffering memory)
    {
        MarginCommon.LoanOffering memory loanOffering = MarginCommon.LoanOffering({
            owedToken: position.owedToken,
            heldToken: position.heldToken,
            payer: addresses[0],
            owner: position.lender,
            taker: addresses[1],
            positionOwner: addresses[2],
            feeRecipient: addresses[3],
            lenderFeeToken: addresses[4],
            takerFeeToken: addresses[5],
            rates: parseLoanOfferingRatesFromIncreasePositionTx(position, values256),
            expirationTimestamp: values256[5],
            callTimeLimit: values32[0],
            maxDuration: values32[1],
            salt: values256[6],
            loanHash: 0,
            signature: signature
        });

        loanOffering.loanHash = MarginCommon.getLoanOfferingHash(loanOffering);

        return loanOffering;
    }

    function parseLoanOfferingRatesFromIncreasePositionTx(
        MarginCommon.Position storage position,
        uint256[8] values256
    )
        private
        view
        returns (MarginCommon.LoanRates memory)
    {
        MarginCommon.LoanRates memory rates = MarginCommon.LoanRates({
            maxAmount: values256[0],
            minAmount: values256[1],
            minHeldToken: values256[2],
            lenderFee: values256[3],
            takerFee: values256[4],
            interestRate: position.interestRate,
            interestPeriod: position.interestPeriod
        });

        return rates;
    }
}

// File: contracts/margin/impl/MarginStorage.sol

/**
 * @title MarginStorage
 * @author dYdX
 *
 * This contract serves as the storage for the entire state of MarginStorage
 */
contract MarginStorage {

    MarginState.State state;

}

// File: contracts/margin/impl/LoanGetters.sol

/**
 * @title LoanGetters
 * @author dYdX
 *
 * A collection of public constant getter functions that allows reading of the state of any loan
 * offering stored in the dYdX protocol.
 */
contract LoanGetters is MarginStorage {

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

    /**
     * Gets the principal amount of a loan offering that is no longer available.
     *
     * @param  loanHash  Unique hash of the loan offering
     * @return           The total unavailable amount of the loan offering, which is equal to the
     *                   filled amount plus the canceled amount.
     */
    function getLoanUnavailableAmount(
        bytes32 loanHash
    )
        external
        view
        returns (uint256)
    {
        return MarginCommon.getUnavailableLoanOfferingAmountImpl(state, loanHash);
    }

    /**
     * Gets the total amount of owed token lent for a loan.
     *
     * @param  loanHash  Unique hash of the loan offering
     * @return           The total filled amount of the loan offering.
     */
    function getLoanFilledAmount(
        bytes32 loanHash
    )
        external
        view
        returns (uint256)
    {
        return state.loanFills[loanHash];
    }

    /**
     * Gets the amount of a loan offering that has been canceled.
     *
     * @param  loanHash  Unique hash of the loan offering
     * @return           The total canceled amount of the loan offering.
     */
    function getLoanCanceledAmount(
        bytes32 loanHash
    )
        external
        view
        returns (uint256)
    {
        return state.loanCancels[loanHash];
    }
}

// File: contracts/margin/interfaces/lender/CancelMarginCallDelegator.sol

/**
 * @title CancelMarginCallDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses cancel a
 * margin-call for a loan owned by the smart contract.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface CancelMarginCallDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call cancelMarginCall().
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that the margin-call was successfully canceled.
     *
     * @param  canceler    Address of the caller of the cancelMarginCall function
     * @param  positionId  Unique ID of the position
     * @return             This address to accept, a different address to ask that contract
     */
    function cancelMarginCallOnBehalfOf(
        address canceler,
        bytes32 positionId
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/interfaces/lender/MarginCallDelegator.sol

/**
 * @title MarginCallDelegator
 * @author dYdX
 *
 * Interface that smart contracts must implement in order to let other addresses margin-call a loan
 * owned by the smart contract.
 *
 * NOTE: Any contract implementing this interface should also use OnlyMargin to control access
 *       to these functions
 */
interface MarginCallDelegator {

    // ============ Public Interface functions ============

    /**
     * Function a contract must implement in order to let other addresses call marginCall().
     *
     * NOTE: If not returning zero (or not reverting), this contract must assume that Margin will
     * either revert the entire transaction or that the loan was successfully margin-called.
     *
     * @param  caller         Address of the caller of the marginCall function
     * @param  positionId     Unique ID of the position
     * @param  depositAmount  Amount of heldToken deposit that will be required to cancel the call
     * @return                This address to accept, a different address to ask that contract
     */
    function marginCallOnBehalfOf(
        address caller,
        bytes32 positionId,
        uint256 depositAmount
    )
        external
        /* onlyMargin */
        returns (address);
}

// File: contracts/margin/impl/LoanImpl.sol

/**
 * @title LoanImpl
 * @author dYdX
 *
 * This library contains the implementation for the following functions of Margin:
 *
 *      - marginCall
 *      - cancelMarginCallImpl
 *      - cancelLoanOffering
 */
library LoanImpl {
    using SafeMath for uint256;

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

    /**
     * A position was margin-called
     */
    event MarginCallInitiated(
        bytes32 indexed positionId,
        address indexed lender,
        address indexed owner,
        uint256 requiredDeposit
    );

    /**
     * A margin call was canceled
     */
    event MarginCallCanceled(
        bytes32 indexed positionId,
        address indexed lender,
        address indexed owner,
        uint256 depositAmount
    );

    /**
     * A loan offering was canceled before it was used. Any amount less than the
     * total for the loan offering can be canceled.
     */
    event LoanOfferingCanceled(
        bytes32 indexed loanHash,
        address indexed payer,
        address indexed feeRecipient,
        uint256 cancelAmount
    );

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

    function marginCallImpl(
        MarginState.State storage state,
        bytes32 positionId,
        uint256 requiredDeposit
    )
        public
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        require(
            position.callTimestamp == 0,
            "LoanImpl#marginCallImpl: The position has already been margin-called"
        );

        // Ensure lender consent
        marginCallOnBehalfOfRecurse(
            position.lender,
            msg.sender,
            positionId,
            requiredDeposit
        );

        position.callTimestamp = TimestampHelper.getBlockTimestamp32();
        position.requiredDeposit = requiredDeposit;

        emit MarginCallInitiated(
            positionId,
            position.lender,
            position.owner,
            requiredDeposit
        );
    }

    function cancelMarginCallImpl(
        MarginState.State storage state,
        bytes32 positionId
    )
        public
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        require(
            position.callTimestamp > 0,
            "LoanImpl#cancelMarginCallImpl: Position has not been margin-called"
        );

        // Ensure lender consent
        cancelMarginCallOnBehalfOfRecurse(
            position.lender,
            msg.sender,
            positionId
        );

        state.positions[positionId].callTimestamp = 0;
        state.positions[positionId].requiredDeposit = 0;

        emit MarginCallCanceled(
            positionId,
            position.lender,
            position.owner,
            0
        );
    }

    function cancelLoanOfferingImpl(
        MarginState.State storage state,
        address[9] addresses,
        uint256[7] values256,
        uint32[4]  values32,
        uint256    cancelAmount
    )
        public
        returns (uint256)
    {
        MarginCommon.LoanOffering memory loanOffering = parseLoanOffering(
            addresses,
            values256,
            values32
        );

        require(
            msg.sender == loanOffering.payer,
            "LoanImpl#cancelLoanOfferingImpl: Only loan offering payer can cancel"
        );
        require(
            loanOffering.expirationTimestamp > block.timestamp,
            "LoanImpl#cancelLoanOfferingImpl: Loan offering has already expired"
        );

        uint256 remainingAmount = loanOffering.rates.maxAmount.sub(
            MarginCommon.getUnavailableLoanOfferingAmountImpl(state, loanOffering.loanHash)
        );
        uint256 amountToCancel = Math.min256(remainingAmount, cancelAmount);

        // If the loan was already fully canceled, then just return 0 amount was canceled
        if (amountToCancel == 0) {
            return 0;
        }

        state.loanCancels[loanOffering.loanHash] =
            state.loanCancels[loanOffering.loanHash].add(amountToCancel);

        emit LoanOfferingCanceled(
            loanOffering.loanHash,
            loanOffering.payer,
            loanOffering.feeRecipient,
            amountToCancel
        );

        return amountToCancel;
    }

    // ============ Private Helper-Functions ============

    function marginCallOnBehalfOfRecurse(
        address contractAddr,
        address who,
        bytes32 positionId,
        uint256 requiredDeposit
    )
        private
    {
        // no need to ask for permission
        if (who == contractAddr) {
            return;
        }

        address newContractAddr =
            MarginCallDelegator(contractAddr).marginCallOnBehalfOf(
                msg.sender,
                positionId,
                requiredDeposit
            );

        if (newContractAddr != contractAddr) {
            marginCallOnBehalfOfRecurse(
                newContractAddr,
                who,
                positionId,
                requiredDeposit
            );
        }
    }

    function cancelMarginCallOnBehalfOfRecurse(
        address contractAddr,
        address who,
        bytes32 positionId
    )
        private
    {
        // no need to ask for permission
        if (who == contractAddr) {
            return;
        }

        address newContractAddr =
            CancelMarginCallDelegator(contractAddr).cancelMarginCallOnBehalfOf(
                msg.sender,
                positionId
            );

        if (newContractAddr != contractAddr) {
            cancelMarginCallOnBehalfOfRecurse(
                newContractAddr,
                who,
                positionId
            );
        }
    }

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

    function parseLoanOffering(
        address[9] addresses,
        uint256[7] values256,
        uint32[4]  values32
    )
        private
        view
        returns (MarginCommon.LoanOffering memory)
    {
        MarginCommon.LoanOffering memory loanOffering = MarginCommon.LoanOffering({
            owedToken: addresses[0],
            heldToken: addresses[1],
            payer: addresses[2],
            owner: addresses[3],
            taker: addresses[4],
            positionOwner: addresses[5],
            feeRecipient: addresses[6],
            lenderFeeToken: addresses[7],
            takerFeeToken: addresses[8],
            rates: parseLoanOfferRates(values256, values32),
            expirationTimestamp: values256[5],
            callTimeLimit: values32[0],
            maxDuration: values32[1],
            salt: values256[6],
            loanHash: 0,
            signature: new bytes(0)
        });

        loanOffering.loanHash = MarginCommon.getLoanOfferingHash(loanOffering);

        return loanOffering;
    }

    function parseLoanOfferRates(
        uint256[7] values256,
        uint32[4] values32
    )
        private
        pure
        returns (MarginCommon.LoanRates memory)
    {
        MarginCommon.LoanRates memory rates = MarginCommon.LoanRates({
            maxAmount: values256[0],
            minAmount: values256[1],
            minHeldToken: values256[2],
            interestRate: values32[2],
            lenderFee: values256[3],
            takerFee: values256[4],
            interestPeriod: values32[3]
        });

        return rates;
    }
}

// File: contracts/margin/impl/MarginAdmin.sol

/**
 * @title MarginAdmin
 * @author dYdX
 *
 * Contains admin functions for the Margin contract
 * The owner can put Margin into various close-only modes, which will disallow new position creation
 */
contract MarginAdmin is Ownable {
    // ============ Enums ============

    // All functionality enabled
    uint8 private constant OPERATION_STATE_OPERATIONAL = 0;

    // Only closing functions + cancelLoanOffering allowed (marginCall, closePosition,
    // cancelLoanOffering, closePositionDirectly, forceRecoverCollateral)
    uint8 private constant OPERATION_STATE_CLOSE_AND_CANCEL_LOAN_ONLY = 1;

    // Only closing functions allowed (marginCall, closePosition, closePositionDirectly,
    // forceRecoverCollateral)
    uint8 private constant OPERATION_STATE_CLOSE_ONLY = 2;

    // Only closing functions allowed (marginCall, closePositionDirectly, forceRecoverCollateral)
    uint8 private constant OPERATION_STATE_CLOSE_DIRECTLY_ONLY = 3;

    // This operation state (and any higher) is invalid
    uint8 private constant OPERATION_STATE_INVALID = 4;

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

    /**
     * Event indicating the operation state has changed
     */
    event OperationStateChanged(
        uint8 from,
        uint8 to
    );

    // ============ State Variables ============

    uint8 public operationState;

    // ============ Constructor ============

    constructor()
        public
        Ownable()
    {
        operationState = OPERATION_STATE_OPERATIONAL;
    }

    // ============ Modifiers ============

    modifier onlyWhileOperational() {
        require(
            operationState == OPERATION_STATE_OPERATIONAL,
            "MarginAdmin#onlyWhileOperational: Can only call while operational"
        );
        _;
    }

    modifier cancelLoanOfferingStateControl() {
        require(
            operationState == OPERATION_STATE_OPERATIONAL
            || operationState == OPERATION_STATE_CLOSE_AND_CANCEL_LOAN_ONLY,
            "MarginAdmin#cancelLoanOfferingStateControl: Invalid operation state"
        );
        _;
    }

    modifier closePositionStateControl() {
        require(
            operationState == OPERATION_STATE_OPERATIONAL
            || operationState == OPERATION_STATE_CLOSE_AND_CANCEL_LOAN_ONLY
            || operationState == OPERATION_STATE_CLOSE_ONLY,
            "MarginAdmin#closePositionStateControl: Invalid operation state"
        );
        _;
    }

    modifier closePositionDirectlyStateControl() {
        _;
    }

    // ============ Owner-Only State-Changing Functions ============

    function setOperationState(
        uint8 newState
    )
        external
        onlyOwner
    {
        require(
            newState < OPERATION_STATE_INVALID,
            "MarginAdmin#setOperationState: newState is not a valid operation state"
        );

        if (newState != operationState) {
            emit OperationStateChanged(
                operationState,
                newState
            );
            operationState = newState;
        }
    }
}

// File: contracts/margin/impl/MarginEvents.sol

/**
 * @title MarginEvents
 * @author dYdX
 *
 * Contains events for the Margin contract.
 *
 * NOTE: Any Margin function libraries that use events will need to both define the event here
 *       and copy the event into the library itself as libraries don't support sharing events
 */
contract MarginEvents {
    // ============ Events ============

    /**
     * A position was opened
     */
    event PositionOpened(
        bytes32 indexed positionId,
        address indexed trader,
        address indexed lender,
        bytes32 loanHash,
        address owedToken,
        address heldToken,
        address loanFeeRecipient,
        uint256 principal,
        uint256 heldTokenFromSell,
        uint256 depositAmount,
        uint256 interestRate,
        uint32  callTimeLimit,
        uint32  maxDuration,
        bool    depositInHeldToken
    );

    /*
     * A position was increased
     */
    event PositionIncreased(
        bytes32 indexed positionId,
        address indexed trader,
        address indexed lender,
        address positionOwner,
        address loanOwner,
        bytes32 loanHash,
        address loanFeeRecipient,
        uint256 amountBorrowed,
        uint256 principalAdded,
        uint256 heldTokenFromSell,
        uint256 depositAmount,
        bool    depositInHeldToken
    );

    /**
     * A position was closed or partially closed
     */
    event PositionClosed(
        bytes32 indexed positionId,
        address indexed closer,
        address indexed payoutRecipient,
        uint256 closeAmount,
        uint256 remainingAmount,
        uint256 owedTokenPaidToLender,
        uint256 payoutAmount,
        uint256 buybackCostInHeldToken,
        bool payoutInHeldToken
    );

    /**
     * Collateral for a position was forcibly recovered
     */
    event CollateralForceRecovered(
        bytes32 indexed positionId,
        address indexed recipient,
        uint256 amount
    );

    /**
     * A position was margin-called
     */
    event MarginCallInitiated(
        bytes32 indexed positionId,
        address indexed lender,
        address indexed owner,
        uint256 requiredDeposit
    );

    /**
     * A margin call was canceled
     */
    event MarginCallCanceled(
        bytes32 indexed positionId,
        address indexed lender,
        address indexed owner,
        uint256 depositAmount
    );

    /**
     * A loan offering was canceled before it was used. Any amount less than the
     * total for the loan offering can be canceled.
     */
    event LoanOfferingCanceled(
        bytes32 indexed loanHash,
        address indexed payer,
        address indexed feeRecipient,
        uint256 cancelAmount
    );

    /**
     * Additional collateral for a position was posted by the owner
     */
    event AdditionalCollateralDeposited(
        bytes32 indexed positionId,
        uint256 amount,
        address depositor
    );

    /**
     * Ownership of a loan was transferred to a new address
     */
    event LoanTransferred(
        bytes32 indexed positionId,
        address indexed from,
        address indexed to
    );

    /**
     * Ownership of a position was transferred to a new address
     */
    event PositionTransferred(
        bytes32 indexed positionId,
        address indexed from,
        address indexed to
    );
}

// File: contracts/margin/impl/OpenPositionImpl.sol

/**
 * @title OpenPositionImpl
 * @author dYdX
 *
 * This library contains the implementation for the openPosition function of Margin
 */
library OpenPositionImpl {
    using SafeMath for uint256;

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

    /**
     * A position was opened
     */
    event PositionOpened(
        bytes32 indexed positionId,
        address indexed trader,
        address indexed lender,
        bytes32 loanHash,
        address owedToken,
        address heldToken,
        address loanFeeRecipient,
        uint256 principal,
        uint256 heldTokenFromSell,
        uint256 depositAmount,
        uint256 interestRate,
        uint32  callTimeLimit,
        uint32  maxDuration,
        bool    depositInHeldToken
    );

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

    function openPositionImpl(
        MarginState.State storage state,
        address[11] addresses,
        uint256[10] values256,
        uint32[4] values32,
        bool depositInHeldToken,
        bytes signature,
        bytes orderData
    )
        public
        returns (bytes32)
    {
        BorrowShared.Tx memory transaction = parseOpenTx(
            addresses,
            values256,
            values32,
            depositInHeldToken,
            signature
        );

        require(
            !MarginCommon.positionHasExisted(state, transaction.positionId),
            "OpenPositionImpl#openPositionImpl: positionId already exists"
        );

        doBorrowAndSell(state, transaction, orderData);

        // Before doStoreNewPosition() so that PositionOpened event is before Transferred events
        recordPositionOpened(
            transaction
        );

        doStoreNewPosition(
            state,
            transaction
        );

        return transaction.positionId;
    }

    // ============ Private Helper-Functions ============

    function doBorrowAndSell(
        MarginState.State storage state,
        BorrowShared.Tx memory transaction,
        bytes orderData
    )
        private
    {
        BorrowShared.validateTxPreSell(state, transaction);

        if (transaction.depositInHeldToken) {
            BorrowShared.doDepositHeldToken(state, transaction);
        } else {
            BorrowShared.doDepositOwedToken(state, transaction);
        }

        transaction.heldTokenFromSell = BorrowShared.doSell(
            state,
            transaction,
            orderData,
            MathHelpers.maxUint256()
        );

        BorrowShared.doPostSell(state, transaction);
    }

    function doStoreNewPosition(
        MarginState.State storage state,
        BorrowShared.Tx memory transaction
    )
        private
    {
        MarginCommon.storeNewPosition(
            state,
            transaction.positionId,
            MarginCommon.Position({
                owedToken: transaction.loanOffering.owedToken,
                heldToken: transaction.loanOffering.heldToken,
                lender: transaction.loanOffering.owner,
                owner: transaction.owner,
                principal: transaction.principal,
                requiredDeposit: 0,
                callTimeLimit: transaction.loanOffering.callTimeLimit,
                startTimestamp: 0,
                callTimestamp: 0,
                maxDuration: transaction.loanOffering.maxDuration,
                interestRate: transaction.loanOffering.rates.interestRate,
                interestPeriod: transaction.loanOffering.rates.interestPeriod
            }),
            transaction.loanOffering.payer
        );
    }

    function recordPositionOpened(
        BorrowShared.Tx transaction
    )
        private
    {
        emit PositionOpened(
            transaction.positionId,
            msg.sender,
            transaction.loanOffering.payer,
            transaction.loanOffering.loanHash,
            transaction.loanOffering.owedToken,
            transaction.loanOffering.heldToken,
            transaction.loanOffering.feeRecipient,
            transaction.principal,
            transaction.heldTokenFromSell,
            transaction.depositAmount,
            transaction.loanOffering.rates.interestRate,
            transaction.loanOffering.callTimeLimit,
            transaction.loanOffering.maxDuration,
            transaction.depositInHeldToken
        );
    }

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

    function parseOpenTx(
        address[11] addresses,
        uint256[10] values256,
        uint32[4] values32,
        bool depositInHeldToken,
        bytes signature
    )
        private
        view
        returns (BorrowShared.Tx memory)
    {
        BorrowShared.Tx memory transaction = BorrowShared.Tx({
            positionId: MarginCommon.getPositionIdFromNonce(values256[9]),
            owner: addresses[0],
            principal: values256[7],
            lenderAmount: values256[7],
            loanOffering: parseLoanOffering(
                addresses,
                values256,
                values32,
                signature
            ),
            exchangeWrapper: addresses[10],
            depositInHeldToken: depositInHeldToken,
            depositAmount: values256[8],
            collateralAmount: 0, // set later
            heldTokenFromSell: 0 // set later
        });

        return transaction;
    }

    function parseLoanOffering(
        address[11] addresses,
        uint256[10] values256,
        uint32[4]   values32,
        bytes       signature
    )
        private
        view
        returns (MarginCommon.LoanOffering memory)
    {
        MarginCommon.LoanOffering memory loanOffering = MarginCommon.LoanOffering({
            owedToken: addresses[1],
            heldToken: addresses[2],
            payer: addresses[3],
            owner: addresses[4],
            taker: addresses[5],
            positionOwner: addresses[6],
            feeRecipient: addresses[7],
            lenderFeeToken: addresses[8],
            takerFeeToken: addresses[9],
            rates: parseLoanOfferRates(values256, values32),
            expirationTimestamp: values256[5],
            callTimeLimit: values32[0],
            maxDuration: values32[1],
            salt: values256[6],
            loanHash: 0,
            signature: signature
        });

        loanOffering.loanHash = MarginCommon.getLoanOfferingHash(loanOffering);

        return loanOffering;
    }

    function parseLoanOfferRates(
        uint256[10] values256,
        uint32[4] values32
    )
        private
        pure
        returns (MarginCommon.LoanRates memory)
    {
        MarginCommon.LoanRates memory rates = MarginCommon.LoanRates({
            maxAmount: values256[0],
            minAmount: values256[1],
            minHeldToken: values256[2],
            lenderFee: values256[3],
            takerFee: values256[4],
            interestRate: values32[2],
            interestPeriod: values32[3]
        });

        return rates;
    }
}

// File: contracts/margin/impl/OpenWithoutCounterpartyImpl.sol

/**
 * @title OpenWithoutCounterpartyImpl
 * @author dYdX
 *
 * This library contains the implementation for the openWithoutCounterparty
 * function of Margin
 */
library OpenWithoutCounterpartyImpl {

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

    struct Tx {
        bytes32 positionId;
        address positionOwner;
        address owedToken;
        address heldToken;
        address loanOwner;
        uint256 principal;
        uint256 deposit;
        uint32 callTimeLimit;
        uint32 maxDuration;
        uint32 interestRate;
        uint32 interestPeriod;
    }

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

    /**
     * A position was opened
     */
    event PositionOpened(
        bytes32 indexed positionId,
        address indexed trader,
        address indexed lender,
        bytes32 loanHash,
        address owedToken,
        address heldToken,
        address loanFeeRecipient,
        uint256 principal,
        uint256 heldTokenFromSell,
        uint256 depositAmount,
        uint256 interestRate,
        uint32  callTimeLimit,
        uint32  maxDuration,
        bool    depositInHeldToken
    );

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

    function openWithoutCounterpartyImpl(
        MarginState.State storage state,
        address[4] addresses,
        uint256[3] values256,
        uint32[4]  values32
    )
        public
        returns (bytes32)
    {
        Tx memory openTx = parseTx(
            addresses,
            values256,
            values32
        );

        validate(
            state,
            openTx
        );

        Vault(state.VAULT).transferToVault(
            openTx.positionId,
            openTx.heldToken,
            msg.sender,
            openTx.deposit
        );

        recordPositionOpened(
            openTx
        );

        doStoreNewPosition(
            state,
            openTx
        );

        return openTx.positionId;
    }

    // ============ Private Helper-Functions ============

    function doStoreNewPosition(
        MarginState.State storage state,
        Tx memory openTx
    )
        private
    {
        MarginCommon.storeNewPosition(
            state,
            openTx.positionId,
            MarginCommon.Position({
                owedToken: openTx.owedToken,
                heldToken: openTx.heldToken,
                lender: openTx.loanOwner,
                owner: openTx.positionOwner,
                principal: openTx.principal,
                requiredDeposit: 0,
                callTimeLimit: openTx.callTimeLimit,
                startTimestamp: 0,
                callTimestamp: 0,
                maxDuration: openTx.maxDuration,
                interestRate: openTx.interestRate,
                interestPeriod: openTx.interestPeriod
            }),
            msg.sender
        );
    }

    function validate(
        MarginState.State storage state,
        Tx memory openTx
    )
        private
        view
    {
        require(
            !MarginCommon.positionHasExisted(state, openTx.positionId),
            "openWithoutCounterpartyImpl#validate: positionId already exists"
        );

        require(
            openTx.principal > 0,
            "openWithoutCounterpartyImpl#validate: principal cannot be 0"
        );

        require(
            openTx.owedToken != address(0),
            "openWithoutCounterpartyImpl#validate: owedToken cannot be 0"
        );

        require(
            openTx.owedToken != openTx.heldToken,
            "openWithoutCounterpartyImpl#validate: owedToken cannot be equal to heldToken"
        );

        require(
            openTx.positionOwner != address(0),
            "openWithoutCounterpartyImpl#validate: positionOwner cannot be 0"
        );

        require(
            openTx.loanOwner != address(0),
            "openWithoutCounterpartyImpl#validate: loanOwner cannot be 0"
        );

        require(
            openTx.maxDuration > 0,
            "openWithoutCounterpartyImpl#validate: maxDuration cannot be 0"
        );

        require(
            openTx.interestPeriod <= openTx.maxDuration,
            "openWithoutCounterpartyImpl#validate: interestPeriod must be <= maxDuration"
        );
    }

    function recordPositionOpened(
        Tx memory openTx
    )
        private
    {
        emit PositionOpened(
            openTx.positionId,
            msg.sender,
            msg.sender,
            bytes32(0),
            openTx.owedToken,
            openTx.heldToken,
            address(0),
            openTx.principal,
            0,
            openTx.deposit,
            openTx.interestRate,
            openTx.callTimeLimit,
            openTx.maxDuration,
            true
        );
    }

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

    function parseTx(
        address[4] addresses,
        uint256[3] values256,
        uint32[4]  values32
    )
        private
        view
        returns (Tx memory)
    {
        Tx memory openTx = Tx({
            positionId: MarginCommon.getPositionIdFromNonce(values256[2]),
            positionOwner: addresses[0],
            owedToken: addresses[1],
            heldToken: addresses[2],
            loanOwner: addresses[3],
            principal: values256[0],
            deposit: values256[1],
            callTimeLimit: values32[0],
            maxDuration: values32[1],
            interestRate: values32[2],
            interestPeriod: values32[3]
        });

        return openTx;
    }
}

// File: contracts/margin/impl/PositionGetters.sol

/**
 * @title PositionGetters
 * @author dYdX
 *
 * A collection of public constant getter functions that allows reading of the state of any position
 * stored in the dYdX protocol.
 */
contract PositionGetters is MarginStorage {
    using SafeMath for uint256;

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

    /**
     * Gets if a position is currently open.
     *
     * @param  positionId  Unique ID of the position
     * @return             True if the position is exists and is open
     */
    function containsPosition(
        bytes32 positionId
    )
        external
        view
        returns (bool)
    {
        return MarginCommon.containsPositionImpl(state, positionId);
    }

    /**
     * Gets if a position is currently margin-called.
     *
     * @param  positionId  Unique ID of the position
     * @return             True if the position is margin-called
     */
    function isPositionCalled(
        bytes32 positionId
    )
        external
        view
        returns (bool)
    {
        return (state.positions[positionId].callTimestamp > 0);
    }

    /**
     * Gets if a position was previously open and is now closed.
     *
     * @param  positionId  Unique ID of the position
     * @return             True if the position is now closed
     */
    function isPositionClosed(
        bytes32 positionId
    )
        external
        view
        returns (bool)
    {
        return state.closedPositions[positionId];
    }

    /**
     * Gets the total amount of owedToken ever repaid to the lender for a position.
     *
     * @param  positionId  Unique ID of the position
     * @return             Total amount of owedToken ever repaid
     */
    function getTotalOwedTokenRepaidToLender(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        return state.totalOwedTokenRepaidToLender[positionId];
    }

    /**
     * Gets the amount of heldToken currently locked up in Vault for a particular position.
     *
     * @param  positionId  Unique ID of the position
     * @return             The amount of heldToken
     */
    function getPositionBalance(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        return MarginCommon.getPositionBalanceImpl(state, positionId);
    }

    /**
     * Gets the time until the interest fee charged for the position will increase.
     * Returns 1 if the interest fee increases every second.
     * Returns 0 if the interest fee will never increase again.
     *
     * @param  positionId  Unique ID of the position
     * @return             The number of seconds until the interest fee will increase
     */
    function getTimeUntilInterestIncrease(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        uint256 effectiveTimeElapsed = MarginCommon.calculateEffectiveTimeElapsed(
            position,
            block.timestamp
        );

        uint256 absoluteTimeElapsed = block.timestamp.sub(position.startTimestamp);
        if (absoluteTimeElapsed > effectiveTimeElapsed) { // past maxDuration
            return 0;
        } else {
            // nextStep is the final second at which the calculated interest fee is the same as it
            // is currently, so add 1 to get the correct value
            return effectiveTimeElapsed.add(1).sub(absoluteTimeElapsed);
        }
    }

    /**
     * Gets the amount of owedTokens currently needed to close the position completely, including
     * interest fees.
     *
     * @param  positionId  Unique ID of the position
     * @return             The number of owedTokens
     */
    function getPositionOwedAmount(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        return MarginCommon.calculateOwedAmount(
            position,
            position.principal,
            block.timestamp
        );
    }

    /**
     * Gets the amount of owedTokens needed to close a given principal amount of the position at a
     * given time, including interest fees.
     *
     * @param  positionId         Unique ID of the position
     * @param  principalToClose   Amount of principal being closed
     * @param  timestamp          Block timestamp in seconds of close
     * @return                    The number of owedTokens owed
     */
    function getPositionOwedAmountAtTime(
        bytes32 positionId,
        uint256 principalToClose,
        uint32  timestamp
    )
        external
        view
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        require(
            timestamp >= position.startTimestamp,
            "PositionGetters#getPositionOwedAmountAtTime: Requested time before position started"
        );

        return MarginCommon.calculateOwedAmount(
            position,
            principalToClose,
            timestamp
        );
    }

    /**
     * Gets the amount of owedTokens that can be borrowed from a lender to add a given principal
     * amount to the position at a given time.
     *
     * @param  positionId      Unique ID of the position
     * @param  principalToAdd  Amount being added to principal
     * @param  timestamp       Block timestamp in seconds of addition
     * @return                 The number of owedTokens that will be borrowed
     */
    function getLenderAmountForIncreasePositionAtTime(
        bytes32 positionId,
        uint256 principalToAdd,
        uint32  timestamp
    )
        external
        view
        returns (uint256)
    {
        MarginCommon.Position storage position =
            MarginCommon.getPositionFromStorage(state, positionId);

        require(
            timestamp >= position.startTimestamp,
            "PositionGetters#getLenderAmountForIncreasePositionAtTime: timestamp < position start"
        );

        return MarginCommon.calculateLenderAmountForIncreasePosition(
            position,
            principalToAdd,
            timestamp
        );
    }

    // ============ All Properties ============

    /**
     * Get a Position by id. This does not validate the position exists. If the position does not
     * exist, all 0's will be returned.
     *
     * @param  positionId  Unique ID of the position
     * @return             Addresses corresponding to:
     *
     *                     [0] = owedToken
     *                     [1] = heldToken
     *                     [2] = lender
     *                     [3] = owner
     *
     *                     Values corresponding to:
     *
     *                     [0] = principal
     *                     [1] = requiredDeposit
     *
     *                     Values corresponding to:
     *
     *                     [0] = callTimeLimit
     *                     [1] = startTimestamp
     *                     [2] = callTimestamp
     *                     [3] = maxDuration
     *                     [4] = interestRate
     *                     [5] = interestPeriod
     */
    function getPosition(
        bytes32 positionId
    )
        external
        view
        returns (
            address[4],
            uint256[2],
            uint32[6]
        )
    {
        MarginCommon.Position storage position = state.positions[positionId];

        return (
            [
                position.owedToken,
                position.heldToken,
                position.lender,
                position.owner
            ],
            [
                position.principal,
                position.requiredDeposit
            ],
            [
                position.callTimeLimit,
                position.startTimestamp,
                position.callTimestamp,
                position.maxDuration,
                position.interestRate,
                position.interestPeriod
            ]
        );
    }

    // ============ Individual Properties ============

    function getPositionLender(
        bytes32 positionId
    )
        external
        view
        returns (address)
    {
        return state.positions[positionId].lender;
    }

    function getPositionOwner(
        bytes32 positionId
    )
        external
        view
        returns (address)
    {
        return state.positions[positionId].owner;
    }

    function getPositionHeldToken(
        bytes32 positionId
    )
        external
        view
        returns (address)
    {
        return state.positions[positionId].heldToken;
    }

    function getPositionOwedToken(
        bytes32 positionId
    )
        external
        view
        returns (address)
    {
        return state.positions[positionId].owedToken;
    }

    function getPositionPrincipal(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        return state.positions[positionId].principal;
    }

    function getPositionInterestRate(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        return state.positions[positionId].interestRate;
    }

    function getPositionRequiredDeposit(
        bytes32 positionId
    )
        external
        view
        returns (uint256)
    {
        return state.positions[positionId].requiredDeposit;
    }

    function getPositionStartTimestamp(
        bytes32 positionId
    )
        external
        view
        returns (uint32)
    {
        return state.positions[positionId].startTimestamp;
    }

    function getPositionCallTimestamp(
        bytes32 positionId
    )
        external
        view
        returns (uint32)
    {
        return state.positions[positionId].callTimestamp;
    }

    function getPositionCallTimeLimit(
        bytes32 positionId
    )
        external
        view
        returns (uint32)
    {
        return state.positions[positionId].callTimeLimit;
    }

    function getPositionMaxDuration(
        bytes32 positionId
    )
        external
        view
        returns (uint32)
    {
        return state.positions[positionId].maxDuration;
    }

    function getPositioninterestPeriod(
        bytes32 positionId
    )
        external
        view
        returns (uint32)
    {
        return state.positions[positionId].interestPeriod;
    }
}

// File: contracts/margin/impl/TransferImpl.sol

/**
 * @title TransferImpl
 * @author dYdX
 *
 * This library contains the implementation for the transferPosition and transferLoan functions of
 * Margin
 */
library TransferImpl {

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

    function transferLoanImpl(
        MarginState.State storage state,
        bytes32 positionId,
        address newLender
    )
        public
    {
        require(
            MarginCommon.containsPositionImpl(state, positionId),
            "TransferImpl#transferLoanImpl: Position does not exist"
        );

        address originalLender = state.positions[positionId].lender;

        require(
            msg.sender == originalLender,
            "TransferImpl#transferLoanImpl: Only lender can transfer ownership"
        );
        require(
            newLender != originalLender,
            "TransferImpl#transferLoanImpl: Cannot transfer ownership to self"
        );

        // Doesn't change the state of positionId; figures out the final owner of loan.
        // That is, newLender may pass ownership to a different address.
        address finalLender = TransferInternal.grantLoanOwnership(
            positionId,
            originalLender,
            newLender);

        require(
            finalLender != originalLender,
            "TransferImpl#transferLoanImpl: Cannot ultimately transfer ownership to self"
        );

        // Set state only after resolving the new owner (to reduce the number of storage calls)
        state.positions[positionId].lender = finalLender;
    }

    function transferPositionImpl(
        MarginState.State storage state,
        bytes32 positionId,
        address newOwner
    )
        public
    {
        require(
            MarginCommon.containsPositionImpl(state, positionId),
            "TransferImpl#transferPositionImpl: Position does not exist"
        );

        address originalOwner = state.positions[positionId].owner;

        require(
            msg.sender == originalOwner,
            "TransferImpl#transferPositionImpl: Only position owner can transfer ownership"
        );
        require(
            newOwner != originalOwner,
            "TransferImpl#transferPositionImpl: Cannot transfer ownership to self"
        );

        // Doesn't change the state of positionId; figures out the final owner of position.
        // That is, newOwner may pass ownership to a different address.
        address finalOwner = TransferInternal.grantPositionOwnership(
            positionId,
            originalOwner,
            newOwner);

        require(
            finalOwner != originalOwner,
            "TransferImpl#transferPositionImpl: Cannot ultimately transfer ownership to self"
        );

        // Set state only after resolving the new owner (to reduce the number of storage calls)
        state.positions[positionId].owner = finalOwner;
    }
}

// File: contracts/margin/Margin.sol

/**
 * @title Margin
 * @author dYdX
 *
 * This contract is used to facilitate margin trading as per the dYdX protocol
 */
contract Margin is
    ReentrancyGuard,
    MarginStorage,
    MarginEvents,
    MarginAdmin,
    LoanGetters,
    PositionGetters
{

    using SafeMath for uint256;

    // ============ Constructor ============

    constructor(
        address vault,
        address proxy
    )
        public
        MarginAdmin()
    {
        state = MarginState.State({
            VAULT: vault,
            TOKEN_PROXY: proxy
        });
    }

    // ============ Public State Changing Functions ============

    /**
     * Open a margin position. Called by the margin trader who must provide both a
     * signed loan offering as well as a DEX Order with which to sell the owedToken.
     *
     * @param  addresses           Addresses corresponding to:
     *
     *  [0]  = position owner
     *  [1]  = owedToken
     *  [2]  = heldToken
     *  [3]  = loan payer
     *  [4]  = loan owner
     *  [5]  = loan taker
     *  [6]  = loan position owner
     *  [7]  = loan fee recipient
     *  [8]  = loan lender fee token
     *  [9]  = loan taker fee token
     *  [10]  = exchange wrapper address
     *
     * @param  values256           Values corresponding to:
     *
     *  [0]  = loan maximum amount
     *  [1]  = loan minimum amount
     *  [2]  = loan minimum heldToken
     *  [3]  = loan lender fee
     *  [4]  = loan taker fee
     *  [5]  = loan expiration timestamp (in seconds)
     *  [6]  = loan salt
     *  [7]  = position amount of principal
     *  [8]  = deposit amount
     *  [9]  = nonce (used to calculate positionId)
     *
     * @param  values32            Values corresponding to:
     *
     *  [0] = loan call time limit (in seconds)
     *  [1] = loan maxDuration (in seconds)
     *  [2] = loan interest rate (annual nominal percentage times 10**6)
     *  [3] = loan interest update period (in seconds)
     *
     * @param  depositInHeldToken  True if the trader wishes to pay the margin deposit in heldToken.
     *                             False if the margin deposit will be in owedToken
     *                             and then sold along with the owedToken borrowed from the lender
     * @param  signature           If loan payer is an account, then this must be the tightly-packed
     *                             ECDSA V/R/S parameters from signing the loan hash. If loan payer
     *                             is a smart contract, these are arbitrary bytes that the contract
     *                             will recieve when choosing whether to approve the loan.
     * @param  order               Order object to be passed to the exchange wrapper
     * @return                     Unique ID for the new position
     */
    function openPosition(
        address[11] addresses,
        uint256[10] values256,
        uint32[4]   values32,
        bool        depositInHeldToken,
        bytes       signature,
        bytes       order
    )
        external
        onlyWhileOperational
        nonReentrant
        returns (bytes32)
    {
        return OpenPositionImpl.openPositionImpl(
            state,
            addresses,
            values256,
            values32,
            depositInHeldToken,
            signature,
            order
        );
    }

    /**
     * Open a margin position without a counterparty. The caller will serve as both the
     * lender and the position owner
     *
     * @param  addresses    Addresses corresponding to:
     *
     *  [0]  = position owner
     *  [1]  = owedToken
     *  [2]  = heldToken
     *  [3]  = loan owner
     *
     * @param  values256    Values corresponding to:
     *
     *  [0]  = principal
     *  [1]  = deposit amount
     *  [2]  = nonce (used to calculate positionId)
     *
     * @param  values32     Values corresponding to:
     *
     *  [0] = call time limit (in seconds)
     *  [1] = maxDuration (in seconds)
     *  [2] = interest rate (annual nominal percentage times 10**6)
     *  [3] = interest update period (in seconds)
     *
     * @return              Unique ID for the new position
     */
    function openWithoutCounterparty(
        address[4] addresses,
        uint256[3] values256,
        uint32[4]  values32
    )
        external
        onlyWhileOperational
        nonReentrant
        returns (bytes32)
    {
        return OpenWithoutCounterpartyImpl.openWithoutCounterpartyImpl(
            state,
            addresses,
            values256,
            values32
        );
    }

    /**
     * Increase the size of a position. Funds will be borrowed from the loan payer and sold as per
     * the position. The amount of owedToken borrowed from the lender will be >= the amount of
     * principal added, as it will incorporate interest already earned by the position so far.
     *
     * @param  positionId          Unique ID of the position
     * @param  addresses           Addresses corresponding to:
     *
     *  [0]  = loan payer
     *  [1]  = loan taker
     *  [2]  = loan position owner
     *  [3]  = loan fee recipient
     *  [4]  = loan lender fee token
     *  [5]  = loan taker fee token
     *  [6]  = exchange wrapper address
     *
     * @param  values256           Values corresponding to:
     *
     *  [0]  = loan maximum amount
     *  [1]  = loan minimum amount
     *  [2]  = loan minimum heldToken
     *  [3]  = loan lender fee
     *  [4]  = loan taker fee
     *  [5]  = loan expiration timestamp (in seconds)
     *  [6]  = loan salt
     *  [7]  = amount of principal to add to the position (NOTE: the amount pulled from the lender
     *                                                           will be >= this amount)
     *
     * @param  values32            Values corresponding to:
     *
     *  [0] = loan call time limit (in seconds)
     *  [1] = loan maxDuration (in seconds)
     *
     * @param  depositInHeldToken  True if the trader wishes to pay the margin deposit in heldToken.
     *                             False if the margin deposit will be pulled in owedToken
     *                             and then sold along with the owedToken borrowed from the lender
     * @param  signature           If loan payer is an account, then this must be the tightly-packed
     *                             ECDSA V/R/S parameters from signing the loan hash. If loan payer
     *                             is a smart contract, these are arbitrary bytes that the contract
     *                             will recieve when choosing whether to approve the loan.
     * @param  order               Order object to be passed to the exchange wrapper
     * @return                     Amount of owedTokens pulled from the lender
     */
    function increasePosition(
        bytes32    positionId,
        address[7] addresses,
        uint256[8] values256,
        uint32[2]  values32,
        bool       depositInHeldToken,
        bytes      signature,
        bytes      order
    )
        external
        onlyWhileOperational
        nonReentrant
        returns (uint256)
    {
        return IncreasePositionImpl.increasePositionImpl(
            state,
            positionId,
            addresses,
            values256,
            values32,
            depositInHeldToken,
            signature,
            order
        );
    }

    /**
     * Increase a position directly by putting up heldToken. The caller will serve as both the
     * lender and the position owner
     *
     * @param  positionId      Unique ID of the position
     * @param  principalToAdd  Principal amount to add to the position
     * @return                 Amount of heldToken pulled from the msg.sender
     */
    function increaseWithoutCounterparty(
        bytes32 positionId,
        uint256 principalToAdd
    )
        external
        onlyWhileOperational
        nonReentrant
        returns (uint256)
    {
        return IncreasePositionImpl.increaseWithoutCounterpartyImpl(
            state,
            positionId,
            principalToAdd
        );
    }

    /**
     * Close a position. May be called by the owner or with the approval of the owner. May provide
     * an order and exchangeWrapper to facilitate the closing of the position. The payoutRecipient
     * is sent the resulting payout.
     *
     * @param  positionId            Unique ID of the position
     * @param  requestedCloseAmount  Principal amount of the position to close. The actual amount
     *                               closed is also bounded by:
     *                               1) The principal of the position
     *                               2) The amount allowed by the owner if closer != owner
     * @param  payoutRecipient       Address of the recipient of tokens paid out from closing
     * @param  exchangeWrapper       Address of the exchange wrapper
     * @param  payoutInHeldToken     True to pay out the payoutRecipient in heldToken,
     *                               False to pay out the payoutRecipient in owedToken
     * @param  order                 Order object to be passed to the exchange wrapper
     * @return                       Values corresponding to:
     *                               1) Principal of position closed
     *                               2) Amount of tokens (heldToken if payoutInHeldtoken is true,
     *                                  owedToken otherwise) received by the payoutRecipient
     *                               3) Amount of owedToken paid (incl. interest fee) to the lender
     */
    function closePosition(
        bytes32 positionId,
        uint256 requestedCloseAmount,
        address payoutRecipient,
        address exchangeWrapper,
        bool    payoutInHeldToken,
        bytes   order
    )
        external
        closePositionStateControl
        nonReentrant
        returns (uint256, uint256, uint256)
    {
        return ClosePositionImpl.closePositionImpl(
            state,
            positionId,
            requestedCloseAmount,
            payoutRecipient,
            exchangeWrapper,
            payoutInHeldToken,
            order
        );
    }

    /**
     * Helper to close a position by paying owedToken directly rather than using an exchangeWrapper.
     *
     * @param  positionId            Unique ID of the position
     * @param  requestedCloseAmount  Principal amount of the position to close. The actual amount
     *                               closed is also bounded by:
     *                               1) The principal of the position
     *                               2) The amount allowed by the owner if closer != owner
     * @param  payoutRecipient       Address of the recipient of tokens paid out from closing
     * @return                       Values corresponding to:
     *                               1) Principal amount of position closed
     *                               2) Amount of heldToken received by the payoutRecipient
     *                               3) Amount of owedToken paid (incl. interest fee) to the lender
     */
    function closePositionDirectly(
        bytes32 positionId,
        uint256 requestedCloseAmount,
        address payoutRecipient
    )
        external
        closePositionDirectlyStateControl
        nonReentrant
        returns (uint256, uint256, uint256)
    {
        return ClosePositionImpl.closePositionImpl(
            state,
            positionId,
            requestedCloseAmount,
            payoutRecipient,
            address(0),
            true,
            new bytes(0)
        );
    }

    /**
     * Reduce the size of a position and withdraw a proportional amount of heldToken from the vault.
     * Must be approved by both the position owner and lender.
     *
     * @param  positionId            Unique ID of the position
     * @param  requestedCloseAmount  Principal amount of the position to close. The actual amount
     *                               closed is also bounded by:
     *                               1) The principal of the position
     *                               2) The amount allowed by the owner if closer != owner
     *                               3) The amount allowed by the lender if closer != lender
     * @return                       Values corresponding to:
     *                               1) Principal amount of position closed
     *                               2) Amount of heldToken received by the msg.sender
     */
    function closeWithoutCounterparty(
        bytes32 positionId,
        uint256 requestedCloseAmount,
        address payoutRecipient
    )
        external
        closePositionStateControl
        nonReentrant
        returns (uint256, uint256)
    {
        return CloseWithoutCounterpartyImpl.closeWithoutCounterpartyImpl(
            state,
            positionId,
            requestedCloseAmount,
            payoutRecipient
        );
    }

    /**
     * Margin-call a position. Only callable with the approval of the position lender. After the
     * call, the position owner will have time equal to the callTimeLimit of the position to close
     * the position. If the owner does not close the position, the lender can recover the collateral
     * in the position.
     *
     * @param  positionId       Unique ID of the position
     * @param  requiredDeposit  Amount of deposit the position owner will have to put up to cancel
     *                          the margin-call. Passing in 0 means the margin call cannot be
     *                          canceled by depositing
     */
    function marginCall(
        bytes32 positionId,
        uint256 requiredDeposit
    )
        external
        nonReentrant
    {
        LoanImpl.marginCallImpl(
            state,
            positionId,
            requiredDeposit
        );
    }

    /**
     * Cancel a margin-call. Only callable with the approval of the position lender.
     *
     * @param  positionId  Unique ID of the position
     */
    function cancelMarginCall(
        bytes32 positionId
    )
        external
        onlyWhileOperational
        nonReentrant
    {
        LoanImpl.cancelMarginCallImpl(state, positionId);
    }

    /**
     * Used to recover the heldTokens held as collateral. Is callable after the maximum duration of
     * the loan has expired or the loan has been margin-called for the duration of the callTimeLimit
     * but remains unclosed. Only callable with the approval of the position lender.
     *
     * @param  positionId  Unique ID of the position
     * @param  recipient   Address to send the recovered tokens to
     * @return             Amount of heldToken recovered
     */
    function forceRecoverCollateral(
        bytes32 positionId,
        address recipient
    )
        external
        nonReentrant
        returns (uint256)
    {
        return ForceRecoverCollateralImpl.forceRecoverCollateralImpl(
            state,
            positionId,
            recipient
        );
    }

    /**
     * Deposit additional heldToken as collateral for a position. Cancels margin-call if:
     * 0 < position.requiredDeposit < depositAmount. Only callable by the position owner.
     *
     * @param  positionId       Unique ID of the position
     * @param  depositAmount    Additional amount in heldToken to deposit
     */
    function depositCollateral(
        bytes32 positionId,
        uint256 depositAmount
    )
        external
        onlyWhileOperational
        nonReentrant
    {
        DepositCollateralImpl.depositCollateralImpl(
            state,
            positionId,
            depositAmount
        );
    }

    /**
     * Cancel an amount of a loan offering. Only callable by the loan offering's payer.
     *
     * @param  addresses     Array of addresses:
     *
     *  [0] = owedToken
     *  [1] = heldToken
     *  [2] = loan payer
     *  [3] = loan owner
     *  [4] = loan taker
     *  [5] = loan position owner
     *  [6] = loan fee recipient
     *  [7] = loan lender fee token
     *  [8] = loan taker fee token
     *
     * @param  values256     Values corresponding to:
     *
     *  [0] = loan maximum amount
     *  [1] = loan minimum amount
     *  [2] = loan minimum heldToken
     *  [3] = loan lender fee
     *  [4] = loan taker fee
     *  [5] = loan expiration timestamp (in seconds)
     *  [6] = loan salt
     *
     * @param  values32      Values corresponding to:
     *
     *  [0] = loan call time limit (in seconds)
     *  [1] = loan maxDuration (in seconds)
     *  [2] = loan interest rate (annual nominal percentage times 10**6)
     *  [3] = loan interest update period (in seconds)
     *
     * @param  cancelAmount  Amount to cancel
     * @return               Amount that was canceled
     */
    function cancelLoanOffering(
        address[9] addresses,
        uint256[7]  values256,
        uint32[4]   values32,
        uint256     cancelAmount
    )
        external
        cancelLoanOfferingStateControl
        nonReentrant
        returns (uint256)
    {
        return LoanImpl.cancelLoanOfferingImpl(
            state,
            addresses,
            values256,
            values32,
            cancelAmount
        );
    }

    /**
     * Transfer ownership of a loan to a new address. This new address will be entitled to all
     * payouts for this loan. Only callable by the lender for a position. If "who" is a contract, it
     * must implement the LoanOwner interface.
     *
     * @param  positionId  Unique ID of the position
     * @param  who         New owner of the loan
     */
    function transferLoan(
        bytes32 positionId,
        address who
    )
        external
        nonReentrant
    {
        TransferImpl.transferLoanImpl(
            state,
            positionId,
            who);
    }

    /**
     * Transfer ownership of a position to a new address. This new address will be entitled to all
     * payouts. Only callable by the owner of a position. If "who" is a contract, it must implement
     * the PositionOwner interface.
     *
     * @param  positionId  Unique ID of the position
     * @param  who         New owner of the position
     */
    function transferPosition(
        bytes32 positionId,
        address who
    )
        external
        nonReentrant
    {
        TransferImpl.transferPositionImpl(
            state,
            positionId,
            who);
    }

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

    /**
     * Gets the address of the Vault contract that holds and accounts for tokens.
     *
     * @return  The address of the Vault contract
     */
    function getVaultAddress()
        external
        view
        returns (address)
    {
        return state.VAULT;
    }

    /**
     * Gets the address of the TokenProxy contract that accounts must set allowance on in order to
     * make loans or open/close positions.
     *
     * @return  The address of the TokenProxy contract
     */
    function getTokenProxyAddress()
        external
        view
        returns (address)
    {
        return state.TOKEN_PROXY;
    }
}

// File: contracts/margin/interfaces/OnlyMargin.sol

/**
 * @title OnlyMargin
 * @author dYdX
 *
 * Contract to store the address of the main Margin contract and trust only that address to call
 * certain functions.
 */
contract OnlyMargin {

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

    // Address of the known and trusted Margin contract on the blockchain
    address public DYDX_MARGIN;

    // ============ Constructor ============

    constructor(
        address margin
    )
        public
    {
        DYDX_MARGIN = margin;
    }

    // ============ Modifiers ============

    modifier onlyMargin()
    {
        require(
            msg.sender == DYDX_MARGIN,
            "OnlyMargin#onlyMargin: Only Margin can call"
        );

        _;
    }
}

// File: contracts/margin/external/interfaces/PositionCustodian.sol

/**
 * @title PositionCustodian
 * @author dYdX
 *
 * Interface to interact with other second-layer contracts. For contracts that own positions as a
 * proxy for other addresses.
 */
interface PositionCustodian {

    /**
     * Function that is intended to be called by external contracts to see where to pay any fees or
     * tokens as a result of closing a position on behalf of another contract.
     *
     * @param  positionId   Unique ID of the position
     * @return              Address of the true owner of the position
     */
    function getPositionDeedHolder(
        bytes32 positionId
    )
        external
        view
        returns (address);
}

// File: contracts/margin/external/lib/MarginHelper.sol

/**
 * @title MarginHelper
 * @author dYdX
 *
 * This library contains helper functions for interacting with Margin
 */
library MarginHelper {
    function getPosition(
        address DYDX_MARGIN,
        bytes32 positionId
    )
        internal
        view
        returns (MarginCommon.Position memory)
    {
        (
            address[4] memory addresses,
            uint256[2] memory values256,
            uint32[6]  memory values32
        ) = Margin(DYDX_MARGIN).getPosition(positionId);

        return MarginCommon.Position({
            owedToken: addresses[0],
            heldToken: addresses[1],
            lender: addresses[2],
            owner: addresses[3],
            principal: values256[0],
            requiredDeposit: values256[1],
            callTimeLimit: values32[0],
            startTimestamp: values32[1],
            callTimestamp: values32[2],
            maxDuration: values32[3],
            interestRate: values32[4],
            interestPeriod: values32[5]
        });
    }
}

// File: contracts/margin/external/ERC20/ERC20Position.sol

/**
 * @title ERC20Position
 * @author dYdX
 *
 * Shared code for ERC20Short and ERC20Long
 */
contract ERC20Position is
    ReentrancyGuard,
    StandardToken,
    OnlyMargin,
    PositionOwner,
    IncreasePositionDelegator,
    ClosePositionDelegator,
    PositionCustodian
{
    using SafeMath for uint256;

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

    enum State {
        UNINITIALIZED,
        OPEN,
        CLOSED
    }

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

    /**
     * This ERC20 was successfully initialized
     */
    event Initialized(
        bytes32 positionId,
        uint256 initialSupply
    );

    /**
     * The position was completely closed by a trusted third-party and tokens can be withdrawn
     */
    event ClosedByTrustedParty(
        address closer,
        uint256 tokenAmount,
        address payoutRecipient
    );

    /**
     * The position was completely closed and tokens can be withdrawn
     */
    event CompletelyClosed();

    /**
     * A user burned tokens to withdraw heldTokens from this contract after the position was closed
     */
    event Withdraw(
        address indexed redeemer,
        uint256 tokensRedeemed,
        uint256 heldTokenPayout
    );

    /**
     * A user burned tokens in order to partially close the position
     */
    event Close(
        address indexed redeemer,
        uint256 closeAmount
    );

    // ============ State Variables ============

    // All tokens will initially be allocated to this address
    address public INITIAL_TOKEN_HOLDER;

    // Unique ID of the position this contract is tokenizing
    bytes32 public POSITION_ID;

    // Recipients that will fairly verify and redistribute funds from closing the position
    mapping (address => bool) public TRUSTED_RECIPIENTS;

    // Withdrawers that will fairly withdraw funds after the position has been closed
    mapping (address => bool) public TRUSTED_WITHDRAWERS;

    // Current State of this contract. See State enum
    State public state;

    // Address of the position's heldToken. Cached for convenience and lower-cost withdrawals
    address public heldToken;

    // Position has been closed using a trusted recipient
    bool public closedUsingTrustedRecipient;

    // ============ Modifiers ============

    modifier onlyPosition(bytes32 positionId) {
        require(
            POSITION_ID == positionId,
            "ERC20Position#onlyPosition: Incorrect position"
        );
        _;
    }

    modifier onlyState(State specificState) {
        require(
            state == specificState,
            "ERC20Position#onlyState: Incorrect State"
        );
        _;
    }

    // ============ Constructor ============

    constructor(
        bytes32 positionId,
        address margin,
        address initialTokenHolder,
        address[] trustedRecipients,
        address[] trustedWithdrawers
    )
        public
        OnlyMargin(margin)
    {
        POSITION_ID = positionId;
        state = State.UNINITIALIZED;
        INITIAL_TOKEN_HOLDER = initialTokenHolder;
        closedUsingTrustedRecipient = false;

        uint256 i;
        for (i = 0; i < trustedRecipients.length; i++) {
            TRUSTED_RECIPIENTS[trustedRecipients[i]] = true;
        }
        for (i = 0; i < trustedWithdrawers.length; i++) {
            TRUSTED_WITHDRAWERS[trustedWithdrawers[i]] = true;
        }
    }

    // ============ Margin-Only Functions ============

    /**
     * Called by Margin when anyone transfers ownership of a position to this contract.
     * This function initializes the tokenization of the position given and returns this address to
     * indicate to Margin that it is willing to take ownership of the position.
     *
     *  param  (unused)
     * @param  positionId  Unique ID of the position
     * @return             This address on success, throw otherwise
     */
    function receivePositionOwnership(
        address /* from */,
        bytes32 positionId
    )
        external
        onlyMargin
        nonReentrant
        onlyState(State.UNINITIALIZED)
        onlyPosition(positionId)
        returns (address)
    {
        MarginCommon.Position memory position = MarginHelper.getPosition(DYDX_MARGIN, POSITION_ID);
        assert(position.principal > 0);

        // set relevant constants
        state = State.OPEN;
        heldToken = position.heldToken;

        uint256 tokenAmount = getTokenAmountOnAdd(position.principal);

        emit Initialized(POSITION_ID, tokenAmount);

        mint(INITIAL_TOKEN_HOLDER, tokenAmount);

        return address(this); // returning own address retains ownership of position
    }

    /**
     * Called by Margin when additional value is added onto the position this contract
     * owns. Tokens are minted and assigned to the address that added the value.
     *
     * @param  trader          Address that added the value to the position
     * @param  positionId      Unique ID of the position
     * @param  principalAdded  Amount that was added to the position
     * @return                 This address on success, throw otherwise
     */
    function increasePositionOnBehalfOf(
        address trader,
        bytes32 positionId,
        uint256 principalAdded
    )
        external
        onlyMargin
        nonReentrant
        onlyState(State.OPEN)
        onlyPosition(positionId)
        returns (address)
    {
        require(
            !Margin(DYDX_MARGIN).isPositionCalled(POSITION_ID),
            "ERC20Position#increasePositionOnBehalfOf: Position is margin-called"
        );
        require(
            !closedUsingTrustedRecipient,
            "ERC20Position#increasePositionOnBehalfOf: Position closed using trusted recipient"
        );

        uint256 tokenAmount = getTokenAmountOnAdd(principalAdded);

        mint(trader, tokenAmount);

        return address(this);
    }

    /**
     * Called by Margin when an owner of this token is attempting to close some of the
     * position. Implementation is required per PositionOwner contract in order to be used by
     * Margin to approve closing parts of a position.
     *
     * @param  closer           Address of the caller of the close function
     * @param  payoutRecipient  Address of the recipient of tokens paid out from closing
     * @param  positionId       Unique ID of the position
     * @param  requestedAmount  Amount (in principal) of the position being closed
     * @return                  1) This address to accept, a different address to ask that contract
     *                          2) The maximum amount that this contract is allowing
     */
    function closeOnBehalfOf(
        address closer,
        address payoutRecipient,
        bytes32 positionId,
        uint256 requestedAmount
    )
        external
        onlyMargin
        nonReentrant
        onlyState(State.OPEN)
        onlyPosition(positionId)
        returns (address, uint256)
    {
        uint256 positionPrincipal = Margin(DYDX_MARGIN).getPositionPrincipal(positionId);

        assert(requestedAmount <= positionPrincipal);

        uint256 allowedAmount;
        if (TRUSTED_RECIPIENTS[payoutRecipient]) {
            allowedAmount = closeUsingTrustedRecipient(
                closer,
                payoutRecipient,
                requestedAmount
            );
        } else {
            allowedAmount = close(
                closer,
                requestedAmount,
                positionPrincipal
            );
        }

        assert(allowedAmount > 0);
        assert(allowedAmount <= requestedAmount);

        if (allowedAmount == positionPrincipal) {
            state = State.CLOSED;
            emit CompletelyClosed();
        }

        return (address(this), allowedAmount);
    }

    // ============ Public State Changing Functions ============

    /**
     * Withdraw heldTokens from this contract for any of the position that was closed via external
     * means (such as an auction-closing mechanism)
     *
     * NOTE: It is possible that this contract could be sent heldToken by external sources
     * other than from the Margin contract. In this case the payout for token holders
     * would be greater than just that from the normal payout. This is fine because
     * nobody has incentive to send this contract extra funds, and if they do then it's
     * also fine just to let the token holders have it.
     *
     * NOTE: If there are significant rounding errors, then it is possible that withdrawing later is
     * more advantageous. An "attack" could involve withdrawing for others before withdrawing for
     * yourself. Likely, rounding error will be small enough to not properly incentivize people to
     * carry out such an attack.
     *
     * @param  onBehalfOf  Address of the account to withdraw for
     * @return             The amount of heldToken withdrawn
     */
    function withdraw(
        address onBehalfOf
    )
        external
        nonReentrant
        returns (uint256)
    {
        setStateClosedIfClosed();
        require(
            state == State.CLOSED,
            "ERC20Position#withdraw: Position has not yet been closed"
        );

        if (msg.sender != onBehalfOf) {
            require(
                TRUSTED_WITHDRAWERS[msg.sender],
                "ERC20Position#withdraw: Only trusted withdrawers can withdraw on behalf of others"
            );
        }

        return withdrawImpl(msg.sender, onBehalfOf);
    }

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

    /**
     * ERC20 name function
     *
     * @return  The name of the Margin Token
     */
    function name()
        external
        view
        returns (string);

    /**
     * ERC20 symbol function
     *
     * @return  The symbol of the Margin Token
     */
    function symbol()
        external
        view
        returns (string);

    /**
     * ERC20 decimals function
     *
     * @return  The number of decimal places
     */
    function decimals()
        external
        view
        returns (uint8);

    /**
     * Implements PositionCustodian functionality. Called by external contracts to see where to pay
     * tokens as a result of closing a position on behalf of this contract
     *
     * @param  positionId  Unique ID of the position
     * @return             Address of this contract. Indicates funds should be sent to this contract
     */
    function getPositionDeedHolder(
        bytes32 positionId
    )
        external
        view
        onlyPosition(positionId)
        returns (address)
    {
        // Claim ownership of deed and allow token holders to withdraw funds from this contract
        return address(this);
    }

    // ============ Internal Helper-Functions ============

    /**
     * Tokens are not burned when a trusted recipient is used, but we require the position to be
     * completely closed. All token holders are then entitled to the heldTokens in the contract
     */
    function closeUsingTrustedRecipient(
        address closer,
        address payoutRecipient,
        uint256 requestedAmount
    )
        internal
        returns (uint256)
    {
        assert(requestedAmount > 0);

        // remember that a trusted recipient was used
        if (!closedUsingTrustedRecipient) {
            closedUsingTrustedRecipient = true;
        }

        emit ClosedByTrustedParty(closer, requestedAmount, payoutRecipient);

        return requestedAmount;
    }

    // ============ Private Helper-Functions ============

    function withdrawImpl(
        address receiver,
        address onBehalfOf
    )
        private
        returns (uint256)
    {
        uint256 value = balanceOf(onBehalfOf);

        if (value == 0) {
            return 0;
        }

        uint256 heldTokenBalance = TokenInteract.balanceOf(heldToken, address(this));

        // NOTE the payout must be calculated before decrementing the totalSupply below
        uint256 heldTokenPayout = MathHelpers.getPartialAmount(
            value,
            totalSupply_,
            heldTokenBalance
        );

        // Destroy the margin tokens
        burn(onBehalfOf, value);
        emit Withdraw(onBehalfOf, value, heldTokenPayout);

        // Send the redeemer their proportion of heldToken
        TokenInteract.transfer(heldToken, receiver, heldTokenPayout);

        return heldTokenPayout;
    }

    function setStateClosedIfClosed(
    )
        private
    {
        // If in OPEN state, but the position is closed, set to CLOSED state
        if (state == State.OPEN && Margin(DYDX_MARGIN).isPositionClosed(POSITION_ID)) {
            state = State.CLOSED;
            emit CompletelyClosed();
        }
    }

    function close(
        address closer,
        uint256 requestedAmount,
        uint256 positionPrincipal
    )
        private
        returns (uint256)
    {
        uint256 balance = balances[closer];

        (
            uint256 tokenAmount,
            uint256 allowedCloseAmount
        ) = getCloseAmounts(
            requestedAmount,
            balance,
            positionPrincipal
        );

        require(
            tokenAmount > 0 && allowedCloseAmount > 0,
            "ERC20Position#close: Cannot close 0 amount"
        );

        assert(allowedCloseAmount <= requestedAmount);

        burn(closer, tokenAmount);

        emit Close(closer, tokenAmount);

        return allowedCloseAmount;
    }

    function burn(
        address from,
        uint256 amount
    )
        private
    {
        assert(from != address(0));
        totalSupply_ = totalSupply_.sub(amount);
        balances[from] = balances[from].sub(amount);
        emit Transfer(from, address(0), amount);
    }

    function mint(
        address to,
        uint256 amount
    )
        private
    {
        assert(to != address(0));
        totalSupply_ = totalSupply_.add(amount);
        balances[to] = balances[to].add(amount);
        emit Transfer(address(0), to, amount);
    }

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

    function getTokenAmountOnAdd(
        uint256 principalAdded
    )
        internal
        view
        returns (uint256);

    function getCloseAmounts(
        uint256 requestedCloseAmount,
        uint256 balance,
        uint256 positionPrincipal
    )
        private
        view
        returns (
            uint256 /* tokenAmount */,
            uint256 /* allowedCloseAmount */
        );
}

// File: contracts/margin/external/ERC20/ERC20CappedPosition.sol

/**
 * @title ERC20CappedPosition
 * @author dYdX
 *
 * ERC20 Position with a limit on the number of tokens that can be minted, and a restriction on
 * which addreses can close the position after it is force-recoverable.
 */
contract ERC20CappedPosition is
    ERC20Position,
    Ownable
{
    using SafeMath for uint256;

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

    event TokenCapSet(
        uint256 tokenCap
    );

    event TrustedCloserSet(
        address closer,
        bool allowed
    );

    // ============ State Variables ============

    mapping(address => bool) public TRUSTED_LATE_CLOSERS;

    uint256 public tokenCap;

    // ============ Constructor ============

    constructor(
        address[] trustedLateClosers,
        uint256 cap
    )
        public
        Ownable()
    {
        for (uint256 i = 0; i < trustedLateClosers.length; i++) {
            TRUSTED_LATE_CLOSERS[trustedLateClosers[i]] = true;
        }
        tokenCap = cap;
    }

    // ============ Owner-Only Functions ============

    function setTokenCap(
        uint256 newCap
    )
        external
        onlyOwner
    {
        // We do not need to require that the tokenCap is >= totalSupply_ because the cap is only
        // checked when increasing the position. It does not prevent any other functionality
        tokenCap = newCap;
        emit TokenCapSet(newCap);
    }

    function setTrustedLateCloser(
        address closer,
        bool allowed
    )
        external
        onlyOwner
    {
        TRUSTED_LATE_CLOSERS[closer] = allowed;
        emit TrustedCloserSet(closer, allowed);
    }

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

    // overrides the function in ERC20Position
    function closeUsingTrustedRecipient(
        address closer,
        address payoutRecipient,
        uint256 requestedAmount
    )
        internal
        returns (uint256)
    {
        MarginCommon.Position memory position = MarginHelper.getPosition(DYDX_MARGIN, POSITION_ID);

        bool afterEnd =
            block.timestamp > uint256(position.startTimestamp).add(position.maxDuration);
        bool afterCall =
            position.callTimestamp > 0 &&
            block.timestamp > uint256(position.callTimestamp).add(position.callTimeLimit);

        if (afterCall || afterEnd) {
            require (
                TRUSTED_LATE_CLOSERS[closer],
                "ERC20CappedPosition#closeUsingTrustedRecipient: closer not in TRUSTED_LATE_CLOSERS"
            );
        }

        return super.closeUsingTrustedRecipient(closer, payoutRecipient, requestedAmount);
    }
}

// File: contracts/lib/StringHelpers.sol

/**
 * @title StringHelpers
 * @author dYdX
 *
 * This library helps with string manipulation in Solidity
 */
library StringHelpers {
    /**
     * Translates a bytes32 to an ascii hexadecimal representation starting with "0x"
     *
     * @param  input  The bytes to convert to hexadecimal
     * @return        A representation of the bytes in ascii hexadecimal
     */
    function bytes32ToHex(
        bytes32 input
    )
        internal
        pure
        returns (bytes)
    {
        uint256 number = uint256(input);
        bytes memory numberAsString = new bytes(66); // "0x" and then 2 chars per byte
        numberAsString[0] = byte(48);  // '0'
        numberAsString[1] = byte(120); // 'x'

        for (uint256 n = 0; n < 32; n++) {
            uint256 nthByte = number / uint256(uint256(2) ** uint256(248 - 8 * n));

            // 1 byte to 2 hexadecimal numbers
            uint8 hex1 = uint8(nthByte) / uint8(16);
            uint8 hex2 = uint8(nthByte) % uint8(16);

            // 87 is ascii for '0', 48 is ascii for 'a'
            hex1 += (hex1 > 9) ? 87 : 48; // shift into proper ascii value
            hex2 += (hex2 > 9) ? 87 : 48; // shift into proper ascii value
            numberAsString[2 * n + 2] = byte(hex1);
            numberAsString[2 * n + 3] = byte(hex2);
        }
        return numberAsString;
    }
}

// File: contracts/margin/external/ERC20/ERC20Long.sol

/**
 * @title ERC20Long
 * @author dYdX
 *
 * Contract used to tokenize leveraged long positions and allow them to be used as ERC20-compliant
 * tokens. Holding the tokens allows the holder to close a piece of the position, or be
 * entitled to some amount of heldTokens after settlement.
 *
 * The total supply of leveraged long tokens is always exactly equal to the number of heldTokens
 * held in collateral in the backing position
 */
contract ERC20Long is ERC20Position {
    constructor(
        bytes32 positionId,
        address margin,
        address initialTokenHolder,
        address[] trustedRecipients,
        address[] trustedWithdrawers
    )
        public
        ERC20Position(
            positionId,
            margin,
            initialTokenHolder,
            trustedRecipients,
            trustedWithdrawers
        )
    {}

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

    function decimals()
        external
        view
        returns (uint8)
    {
        return DetailedERC20(heldToken).decimals();
    }

    function symbol()
        external
        view
        returns (string)
    {
        if (state == State.UNINITIALIZED) {
            return "L[UNINITIALIZED]";
        }
        return string(
            abi.encodePacked(
                "L",
                DetailedERC20(heldToken).symbol()
            )
        );
    }

    function name()
        external
        view
        returns (string)
    {
        if (state == State.UNINITIALIZED) {
            return "dYdX Leveraged Long Token [UNINITIALIZED]";
        }
        return string(
            abi.encodePacked(
                "dYdX Leveraged Long Token ",
                StringHelpers.bytes32ToHex(POSITION_ID)
            )
        );
    }

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

    function getTokenAmountOnAdd(
        uint256 /* principalAdded */
    )
        internal
        view
        returns (uint256)
    {
        // total supply should always equal position balance, except after closing with trusted
        // recipient, in which case this function cannot be called.

        uint256 positionBalance = Margin(DYDX_MARGIN).getPositionBalance(POSITION_ID);
        return positionBalance.sub(totalSupply_);
    }

    function getCloseAmounts(
        uint256 requestedCloseAmount,
        uint256 balance,
        uint256 positionPrincipal
    )
        private
        view
        returns (
            uint256 /* tokenAmount */,
            uint256 /* allowedCloseAmount */
        )
    {
        uint256 positionBalance = Margin(DYDX_MARGIN).getPositionBalance(POSITION_ID);

        uint256 requestedTokenAmount = MathHelpers.getPartialAmount(
            requestedCloseAmount,
            positionPrincipal,
            positionBalance
        );

        // if user has enough tokens, allow the close to occur
        if (requestedTokenAmount <= balance) {
            return (requestedTokenAmount, requestedCloseAmount);
        }

        // The maximum amount of principal able to be closed without using more heldTokens
        // than balance
        uint256 allowedCloseAmount = MathHelpers.getPartialAmount(
            balance,
            positionBalance,
            positionPrincipal
        );

        // the new close amount should not be higher than what was requested
        assert(allowedCloseAmount < requestedCloseAmount);

        uint256 allowedTokenAmount = MathHelpers.getPartialAmount(
            allowedCloseAmount,
            positionPrincipal,
            positionBalance
        );

        return (allowedTokenAmount, allowedCloseAmount);
    }
}

// File: contracts/margin/external/ERC20/ERC20CappedLong.sol

/**
 * @title ERC20CappedLong
 * @author dYdX
 *
 * ERC20Long with a limit on the number of tokens that can be minted, and a restriction on
 * which addreses can close the position after it is force-recoverable.
 */
contract ERC20CappedLong is
    ERC20Long,
    ERC20CappedPosition,
    DetailedERC20
{
    using SafeMath for uint256;

    // ============ Constructor ============

    constructor(
        bytes32 positionId,
        address margin,
        address initialTokenHolder,
        address[] trustedRecipients,
        address[] trustedWithdrawers,
        address[] trustedLateClosers,
        uint256 cap,
        string name,
        string symbol,
        uint8 decimals
    )
        public
        ERC20Long(
            positionId,
            margin,
            initialTokenHolder,
            trustedRecipients,
            trustedWithdrawers
        )
        ERC20CappedPosition(
            trustedLateClosers,
            cap
        )
        DetailedERC20(
            name,
            symbol,
            decimals
        )
    {
    }

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

    function getTokenAmountOnAdd(
        uint256 principalAdded
    )
        internal
        view
        returns (uint256)
    {
        uint256 tokenAmount = super.getTokenAmountOnAdd(principalAdded);

        require(
            totalSupply_.add(tokenAmount) <= tokenCap,
            "ERC20CappedLong#getTokenAmountOnAdd: Adding tokenAmount would exceed cap"
        );

        return tokenAmount;
    }
}

Contract Security Audit

Contract ABI

[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"DYDX_MARGIN","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newCap","type":"uint256"}],"name":"setTokenCap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"closer","type":"address"},{"name":"payoutRecipient","type":"address"},{"name":"positionId","type":"bytes32"},{"name":"requestedAmount","type":"uint256"}],"name":"closeOnBehalfOf","outputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"TRUSTED_LATE_CLOSERS","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"positionId","type":"bytes32"}],"name":"getPositionDeedHolder","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"onBehalfOf","type":"address"}],"name":"withdraw","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"TRUSTED_RECIPIENTS","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"closedUsingTrustedRecipient","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"INITIAL_TOKEN_HOLDER","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"TRUSTED_WITHDRAWERS","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"POSITION_ID","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"trader","type":"address"},{"name":"positionId","type":"bytes32"},{"name":"principalAdded","type":"uint256"}],"name":"increasePositionOnBehalfOf","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"heldToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"","type":"address"},{"name":"positionId","type":"bytes32"}],"name":"receivePositionOwnership","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"closer","type":"address"},{"name":"allowed","type":"bool"}],"name":"setTrustedLateCloser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"positionId","type":"bytes32"},{"name":"margin","type":"address"},{"name":"initialTokenHolder","type":"address"},{"name":"trustedRecipients","type":"address[]"},{"name":"trustedWithdrawers","type":"address[]"},{"name":"trustedLateClosers","type":"address[]"},{"name":"cap","type":"uint256"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokenCap","type":"uint256"}],"name":"TokenCapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"closer","type":"address"},{"indexed":false,"name":"allowed","type":"bool"}],"name":"TrustedCloserSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"positionId","type":"bytes32"},{"indexed":false,"name":"initialSupply","type":"uint256"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"closer","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"payoutRecipient","type":"address"}],"name":"ClosedByTrustedParty","type":"event"},{"anonymous":false,"inputs":[],"name":"CompletelyClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"redeemer","type":"address"},{"indexed":false,"name":"tokensRedeemed","type":"uint256"},{"indexed":false,"name":"heldTokenPayout","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"redeemer","type":"address"},{"indexed":false,"name":"closeAmount","type":"uint256"}],"name":"Close","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]

608060405260016000553480156200001657600080fd5b506040516200334f3803806200334f83398101806040528101908080519060200190929190805190602001909291908051906020019092919080518201929190602001805182019291906020018051820192919060200180519060200190929190805182019291906020018051820192919060200180519060200190929190505050828282878760008f8f8f8f8f848484848460008480600460006101000a815481600160a060020a030219169083600160a060020a031602179055505085600681600019169055506000600960006101000a81548160ff02191690836002811115620000ff57fe5b02179055505060058054600160a060020a031916600160a060020a0385161790556009805460a860020a60ff021916905560005b82518110156200018d5760016007600085848151811015156200015257fe5b602090810291909101810151600160a060020a03168252810191909152604001600020805460ff191691151591909117905560010162000133565b5060005b8151811015620001eb576001600860008484815181101515620001b057fe5b602090810291909101810151600160a060020a03168252810191909152604001600020805460ff191691151591909117905560010162000191565b5050600a8054600160a060020a03191633179055506000985050505050505050505b825181101562000267576001600b600085848151811015156200022c57fe5b602090810291909101810151600160a060020a03168252810191909152604001600020805460ff19169115159190911790556001016200020d565b50600c555082516200028190600d906020860190620002bf565b5081516200029790600e906020850190620002bf565b50600f805460ff191660ff9290921691909117905550620003649a5050505050505050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200030257805160ff191683800117855562000332565b8280016001018555821562000332579182015b828111156200033257825182559160200191906001019062000315565b506200034092915062000344565b5090565b6200036191905b808211156200034057600081556001016200034b565b90565b612fdb80620003746000396000f30060806040526004361061018a5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461018f578063095ea7b3146102195780630dda60cc1461025e57806318160ddd1461029c57806323b872dd146102c35780632854bc7e146102fa578063313ce5671461031457806335ba1d0a1461033f5780633cd29c46146103a957806347369a7c146103d757806351cff8d9146103ef578063661884631461041d57806370a082311461044e578063715018a61461047c5780638da5cb5b14610491578063959ca8ab146104a657806395d89b41146104d4578063a9059cbb146104e9578063ae08fa551461051a578063bcfa07861461052f578063bd7456e314610544578063c19d93fb14610572578063d73dd623146105ab578063d7ac71ff146105dc578063db4c528b146105f1578063dd54291b14610625578063dd56898c1461063a578063dd62ed3e1461064f578063e25bd40914610683578063f2fde38b146106b4578063f70b0a07146106e2575b600080fd5b34801561019b57600080fd5b506101a4610715565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101de5781810151838201526020016101c6565b50505050905090810190601f16801561020b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561022557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff600435166024356107c1565b604080519115158252519081900360200190f35b34801561026a57600080fd5b50610273610835565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156102a857600080fd5b506102b1610851565b60408051918252519081900360200190f35b3480156102cf57600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610857565b34801561030657600080fd5b50610312600435610a29565b005b34801561032057600080fd5b50610329610a88565b6040805160ff9092168252519081900360200190f35b34801561034b57600080fd5b5061037973ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435606435610a91565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091528051918290030190f35b3480156103b557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043516610e7a565b3480156103e357600080fd5b50610273600435610e8f565b3480156103fb57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043516610f34565b34801561042957600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043516602435611150565b34801561045a57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043516611273565b34801561048857600080fd5b5061031261129b565b34801561049d57600080fd5b5061027361132e565b3480156104b257600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351661134a565b3480156104e057600080fd5b506101a461135f565b3480156104f557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff600435166024356113d8565b34801561052657600080fd5b5061024a6114e0565b34801561053b57600080fd5b50610273611502565b34801561055057600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351661151e565b34801561057e57600080fd5b50610587611533565b6040518082600281111561059757fe5b60ff16815260200191505060405180910390f35b3480156105b757600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351660243561153c565b3480156105e857600080fd5b506102b16115ef565b3480156105fd57600080fd5b5061027373ffffffffffffffffffffffffffffffffffffffff600435166024356044356115f5565b34801561063157600080fd5b506102b1611ab1565b34801561064657600080fd5b50610273611ab7565b34801561065b57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043581169060243516611ad8565b34801561068f57600080fd5b5061027373ffffffffffffffffffffffffffffffffffffffff60043516602435611b10565b3480156106c057600080fd5b5061031273ffffffffffffffffffffffffffffffffffffffff60043516611ea8565b3480156106ee57600080fd5b5061031273ffffffffffffffffffffffffffffffffffffffff600435166024351515611ed8565b600d805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f810184900484028201840190925281815292918301828280156107b95780601f1061078e576101008083540402835291602001916107b9565b820191906000526020600020905b81548152906001019060200180831161079c57829003601f168201915b505050505081565b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60045473ffffffffffffffffffffffffffffffffffffffff1681565b60025490565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081205482111561088957600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526003602090815260408083203384529091529020548211156108c657600080fd5b73ffffffffffffffffffffffffffffffffffffffff831615156108e857600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090205461091e908363ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600160205260408082209390935590851681522054610960908363ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526001602090815260408083209490945591871681526003825282812033825290915220546109b1908363ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff808616600081815260036020908152604080832033845282529182902094909455805186815290519287169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060019392505050565b600a5473ffffffffffffffffffffffffffffffffffffffff163314610a4d57600080fd5b600c8190556040805182815290517fb8b1d1cfbb7d9887f2842719df47280b1956879afc2e4ecd0cf92fbb5e932fd29181900360200190a150565b600f5460ff1681565b60045460009081908190819073ffffffffffffffffffffffffffffffffffffffff163314610b4657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b600080546001908101918290558060095460ff166002811115610b6557fe5b14610bf757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60065488908114610c8f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b60048054604080517f0e8a4ac70000000000000000000000000000000000000000000000000000000081529283018c90525173ffffffffffffffffffffffffffffffffffffffff90911691630e8a4ac7916024808301926020929190829003018186803b158015610cff57600080fd5b505afa158015610d13573d6000803e3d6000fd5b505050506040513d6020811015610d2957600080fd5b5051945084881115610d3757fe5b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526007602052604090205460ff1615610d7757610d708b8b8a611faa565b9350610d85565b610d828b8987612156565b93505b60008411610d8f57fe5b87841115610d9957fe5b84841415610df657600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660021790556040517fb147077378b3c0311f1c5a608d4cb621068096d16e8b1fdd1f1157ebc146306490600090a15b30849650965050506000548114610e6e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50505094509492505050565b600b6020526000908152604090205460ff1681565b60065460009082908114610f2a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b3091505b50919050565b60008054600101808255610f4661229a565b600260095460ff166002811115610f5957fe5b14610feb57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4552433230506f736974696f6e2377697468647261773a20506f736974696f6e60448201527f20686173206e6f7420796574206265656e20636c6f7365640000000000000000606482015290519081900360840190fd5b3373ffffffffffffffffffffffffffffffffffffffff8416146110d4573360009081526008602052604090205460ff1615156110d457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4552433230506f736974696f6e2377697468647261773a204f6e6c792074727560448201527f737465642077697468647261776572732063616e207769746864726177206f6e60648201527f20626568616c66206f66206f7468657273000000000000000000000000000000608482015290519081900360a40190fd5b6110de33846123b2565b91506000548114610f2e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091528120548083106111be5733600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff88168452909152812055611200565b6111ce818463ffffffff611f8b16565b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff891684529091529020555b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b600a5473ffffffffffffffffffffffffffffffffffffffff1633146112bf57600080fd5b600a5460405173ffffffffffffffffffffffffffffffffffffffff909116907ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482090600090a2600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600a5473ffffffffffffffffffffffffffffffffffffffff1681565b60076020526000908152604090205460ff1681565b600e805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f810184900484028201840190925281815292918301828280156107b95780601f1061078e576101008083540402835291602001916107b9565b336000908152600160205260408120548211156113f457600080fd5b73ffffffffffffffffffffffffffffffffffffffff8316151561141657600080fd5b33600090815260016020526040902054611436908363ffffffff611f8b16565b336000908152600160205260408082209290925573ffffffffffffffffffffffffffffffffffffffff851681522054611475908363ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350600192915050565b6009547501000000000000000000000000000000000000000000900460ff1681565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60086020526000908152604090205460ff1681565b60095460ff1681565b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915281205461157d908363ffffffff611f9d16565b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b60065481565b600454600090819073ffffffffffffffffffffffffffffffffffffffff1633146116a657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b600080546001908101918290558060095460ff1660028111156116c557fe5b1461175757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600654869081146117ef57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b60048054600654604080517f6e0cd415000000000000000000000000000000000000000000000000000000008152938401919091525173ffffffffffffffffffffffffffffffffffffffff90911691636e0cd415916024808301926020929190829003018186803b15801561186357600080fd5b505afa158015611877573d6000803e3d6000fd5b505050506040513d602081101561188d57600080fd5b50511561194757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4552433230506f736974696f6e23696e637265617365506f736974696f6e4f6e60448201527f426568616c664f663a20506f736974696f6e206973206d617267696e2d63616c60648201527f6c65640000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b6009547501000000000000000000000000000000000000000000900460ff1615611a1e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4552433230506f736974696f6e23696e637265617365506f736974696f6e4f6e60448201527f426568616c664f663a20506f736974696f6e20636c6f736564207573696e672060648201527f7472757374656420726563697069656e74000000000000000000000000000000608482015290519081900360a40190fd5b611a2786612498565b9350611a338885612577565b30945050506000548114611aa857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50509392505050565b600c5481565b600954610100900473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260036020908152604080832093909416825291909152205490565b6000611b1a612ef2565b60045460009073ffffffffffffffffffffffffffffffffffffffff163314611bc957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b60008054600101808255908060095460ff166002811115611be657fe5b14611c7857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60065486908114611d1057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b600454600654611d369173ffffffffffffffffffffffffffffffffffffffff1690612647565b945060008560800151111515611d4857fe5b60098054602087015173ffffffffffffffffffffffffffffffffffffffff16610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909216600117919091161790556080850151611dc890612498565b600654604080519182526020820183905280519296507fc885ca8035bb269b79b1906eb7aaeeec6a4713b0976b175c25aa84b49acfa47392918290030190a1600554611e2a9073ffffffffffffffffffffffffffffffffffffffff1685612577565b30955050506000548114611e9f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50505092915050565b600a5473ffffffffffffffffffffffffffffffffffffffff163314611ecc57600080fd5b611ed5816127d5565b50565b600a5473ffffffffffffffffffffffffffffffffffffffff163314611efc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600b602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915582519384529083015280517f4f6c69ea5a0543c614b665fb438ce26ed0e5607ecdb69820ac336c815a852d6f9281900390910190a15050565b600082821115611f9757fe5b50900390565b8181018281101561082f57fe5b6000611fb4612ef2565b6004546006546000918291611fdf9173ffffffffffffffffffffffffffffffffffffffff1690612647565b925061200983610120015163ffffffff168460e0015163ffffffff16611f9d90919063ffffffff16565b42119150600083610100015163ffffffff1611801561204e575061204b8360c0015163ffffffff1684610100015163ffffffff16611f9d90919063ffffffff16565b42115b905080806120595750815b156121405773ffffffffffffffffffffffffffffffffffffffff87166000908152600b602052604090205460ff16151561214057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605260248201527f4552433230436170706564506f736974696f6e23636c6f73655573696e67547260448201527f7573746564526563697069656e743a20636c6f736572206e6f7420696e20545260648201527f55535445445f4c4154455f434c4f534552530000000000000000000000000000608482015290519081900360a40190fd5b61214b878787612885565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120548180612189868487612954565b9150915060008211801561219d5750600081115b151561223057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f4552433230506f736974696f6e23636c6f73653a2043616e6e6f7420636c6f7360448201527f65203020616d6f756e7400000000000000000000000000000000000000000000606482015290519081900360840190fd5b8581111561223a57fe5b6122448783612a58565b60408051838152905173ffffffffffffffffffffffffffffffffffffffff8916917f684222b0069d4a2e5e0d986611cc5182d543904c4e4264bf770d4e51faefc822919081900360200190a29695505050505050565b600160095460ff1660028111156122ad57fe5b148015612356575060048054600654604080517f640075f3000000000000000000000000000000000000000000000000000000008152938401919091525173ffffffffffffffffffffffffffffffffffffffff9091169163640075f3916024808301926020929190829003018186803b15801561232957600080fd5b505afa15801561233d573d6000803e3d6000fd5b505050506040513d602081101561235357600080fd5b50515b156123b057600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660021790556040517fb147077378b3c0311f1c5a608d4cb621068096d16e8b1fdd1f1157ebc146306490600090a15b565b6000806000806123c185611273565b92508215156123d35760009350611e9f565b6009546123fb90610100900473ffffffffffffffffffffffffffffffffffffffff1630612b27565b915061240a8360025484612bf5565b90506124168584612a58565b6040805184815260208101839052815173ffffffffffffffffffffffffffffffffffffffff8816927ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568928290030190a260095461248f90610100900473ffffffffffffffffffffffffffffffffffffffff168783612c1f565b95945050505050565b6000806124a483612dbe565b9050600c546124be82600254611f9d90919063ffffffff16565b111561082f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f45524332304361707065644c6f6e6723676574546f6b656e416d6f756e744f6e60448201527f4164643a20416464696e6720746f6b656e416d6f756e7420776f756c6420657860648201527f6365656420636170000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b73ffffffffffffffffffffffffffffffffffffffff8216151561259657fe5b6002546125a9908263ffffffff611f9d16565b60025573ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546125e2908263ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b61264f612ef2565b612657612f56565b61265f612f75565b612667612f90565b604080517f1928b3cb00000000000000000000000000000000000000000000000000000000815260048101879052905173ffffffffffffffffffffffffffffffffffffffff881691631928b3cb91602480830192610180929190829003018186803b1580156126d557600080fd5b505afa1580156126e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061018081101561270f57600080fd5b506040805161018081018252825173ffffffffffffffffffffffffffffffffffffffff908116825260208085015182169083015283830151811692820192909252606080840151909216918101919091526080808301519082015260a0808301519082015260c08083015163ffffffff9081169183019190915260e0808401518216908301526101008084015182169083015261012080840151821690830152610140808401518216908301526101609283015116918101919091529695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811615156127f757600080fd5b600a5460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600080821161289057fe5b6009547501000000000000000000000000000000000000000000900460ff1615156128f657600980547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b6040805173ffffffffffffffffffffffffffffffffffffffff80871682526020820185905285168183015290517f2a73d58687443b7a81f739a2964d5cf5068b313c8756ae6726b20d09926f864b9181900360600190a15092915050565b60048054600654604080517f54d7986800000000000000000000000000000000000000000000000000000000815293840191909152516000928392839283928392839273ffffffffffffffffffffffffffffffffffffffff909116916354d79868916024808301926020929190829003018186803b1580156129d557600080fd5b505afa1580156129e9573d6000803e3d6000fd5b505050506040513d60208110156129ff57600080fd5b50519350612a0e898886612bf5565b9250878311612a2257828995509550612a4c565b612a2d888589612bf5565b9150888210612a3857fe5b612a43828886612bf5565b90508082955095505b50505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff82161515612a7757fe5b600254612a8a908263ffffffff611f8b16565b60025573ffffffffffffffffffffffffffffffffffffffff8216600090815260016020526040902054612ac3908263ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600160209081526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231836040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612bc257600080fd5b505afa158015612bd6573d6000803e3d6000fd5b505050506040513d6020811015612bec57600080fd5b50519392505050565b6000612c1783612c0b868563ffffffff612e8016565b9063ffffffff612ea916565b949350505050565b30811580612c5857508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b15612c6257612db8565b8373ffffffffffffffffffffffffffffffffffffffff1663a9059cbb84846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b158015612d0557600080fd5b505af1158015612d19573d6000803e3d6000fd5b50505050612d25612ebe565b1515612db857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e496e746572616374237472616e736665723a205472616e7366657260448201527f206661696c656400000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b50505050565b60048054600654604080517f54d798680000000000000000000000000000000000000000000000000000000081529384019190915251600092839273ffffffffffffffffffffffffffffffffffffffff16916354d7986891602480820192602092909190829003018186803b158015612e3657600080fd5b505afa158015612e4a573d6000803e3d6000fd5b505050506040513d6020811015612e6057600080fd5b5051600254909150612e7990829063ffffffff611f8b16565b9392505050565b6000821515612e915750600061082f565b50818102818382811515612ea157fe5b041461082f57fe5b60008183811515612eb657fe5b049392505050565b6000803d8015612ed55760208114612ede57612eea565b60019150612eea565b60206000803e60005191505b501515919050565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b6080604051908101604052806004906020820280388339509192915050565b60408051808201825290600290829080388339509192915050565b60c06040519081016040528060069060208202803883395091929150505600a165627a7a72305820febb5ce4d6cb0de254a2396f5755d91f4ff803dba5cc2b40eda73a2b701e45a0002920362f9b989a5be5f8766507dc01e2e8398f52f3910b84ab125ed775fffaa1a000000000000000000000000036bf21c8e661b21e6166e4385f574941fdc6caff000000000000000000000000ac89e378758c97625d5448065d92f63f4851f1e20000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000005c8271e493541a00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000686f7d0b698b6780a8ce1d6ae13bb8c813552ff30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f57263953d4d02707cc26b64b920820cd310a01d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000007be6cfa395d9cded31705e12dcfba6276a31e6600000000000000000000000000000000000000000000000000000000000000134c6576657261676564204554482031322f313500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4c4554482031322f313500000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361061018a5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde03811461018f578063095ea7b3146102195780630dda60cc1461025e57806318160ddd1461029c57806323b872dd146102c35780632854bc7e146102fa578063313ce5671461031457806335ba1d0a1461033f5780633cd29c46146103a957806347369a7c146103d757806351cff8d9146103ef578063661884631461041d57806370a082311461044e578063715018a61461047c5780638da5cb5b14610491578063959ca8ab146104a657806395d89b41146104d4578063a9059cbb146104e9578063ae08fa551461051a578063bcfa07861461052f578063bd7456e314610544578063c19d93fb14610572578063d73dd623146105ab578063d7ac71ff146105dc578063db4c528b146105f1578063dd54291b14610625578063dd56898c1461063a578063dd62ed3e1461064f578063e25bd40914610683578063f2fde38b146106b4578063f70b0a07146106e2575b600080fd5b34801561019b57600080fd5b506101a4610715565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101de5781810151838201526020016101c6565b50505050905090810190601f16801561020b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561022557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff600435166024356107c1565b604080519115158252519081900360200190f35b34801561026a57600080fd5b50610273610835565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156102a857600080fd5b506102b1610851565b60408051918252519081900360200190f35b3480156102cf57600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610857565b34801561030657600080fd5b50610312600435610a29565b005b34801561032057600080fd5b50610329610a88565b6040805160ff9092168252519081900360200190f35b34801561034b57600080fd5b5061037973ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435606435610a91565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091528051918290030190f35b3480156103b557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043516610e7a565b3480156103e357600080fd5b50610273600435610e8f565b3480156103fb57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043516610f34565b34801561042957600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff60043516602435611150565b34801561045a57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043516611273565b34801561048857600080fd5b5061031261129b565b34801561049d57600080fd5b5061027361132e565b3480156104b257600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351661134a565b3480156104e057600080fd5b506101a461135f565b3480156104f557600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff600435166024356113d8565b34801561052657600080fd5b5061024a6114e0565b34801561053b57600080fd5b50610273611502565b34801561055057600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351661151e565b34801561057e57600080fd5b50610587611533565b6040518082600281111561059757fe5b60ff16815260200191505060405180910390f35b3480156105b757600080fd5b5061024a73ffffffffffffffffffffffffffffffffffffffff6004351660243561153c565b3480156105e857600080fd5b506102b16115ef565b3480156105fd57600080fd5b5061027373ffffffffffffffffffffffffffffffffffffffff600435166024356044356115f5565b34801561063157600080fd5b506102b1611ab1565b34801561064657600080fd5b50610273611ab7565b34801561065b57600080fd5b506102b173ffffffffffffffffffffffffffffffffffffffff60043581169060243516611ad8565b34801561068f57600080fd5b5061027373ffffffffffffffffffffffffffffffffffffffff60043516602435611b10565b3480156106c057600080fd5b5061031273ffffffffffffffffffffffffffffffffffffffff60043516611ea8565b3480156106ee57600080fd5b5061031273ffffffffffffffffffffffffffffffffffffffff600435166024351515611ed8565b600d805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f810184900484028201840190925281815292918301828280156107b95780601f1061078e576101008083540402835291602001916107b9565b820191906000526020600020905b81548152906001019060200180831161079c57829003601f168201915b505050505081565b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60045473ffffffffffffffffffffffffffffffffffffffff1681565b60025490565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081205482111561088957600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526003602090815260408083203384529091529020548211156108c657600080fd5b73ffffffffffffffffffffffffffffffffffffffff831615156108e857600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090205461091e908363ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600160205260408082209390935590851681522054610960908363ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526001602090815260408083209490945591871681526003825282812033825290915220546109b1908363ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff808616600081815260036020908152604080832033845282529182902094909455805186815290519287169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060019392505050565b600a5473ffffffffffffffffffffffffffffffffffffffff163314610a4d57600080fd5b600c8190556040805182815290517fb8b1d1cfbb7d9887f2842719df47280b1956879afc2e4ecd0cf92fbb5e932fd29181900360200190a150565b600f5460ff1681565b60045460009081908190819073ffffffffffffffffffffffffffffffffffffffff163314610b4657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b600080546001908101918290558060095460ff166002811115610b6557fe5b14610bf757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60065488908114610c8f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b60048054604080517f0e8a4ac70000000000000000000000000000000000000000000000000000000081529283018c90525173ffffffffffffffffffffffffffffffffffffffff90911691630e8a4ac7916024808301926020929190829003018186803b158015610cff57600080fd5b505afa158015610d13573d6000803e3d6000fd5b505050506040513d6020811015610d2957600080fd5b5051945084881115610d3757fe5b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526007602052604090205460ff1615610d7757610d708b8b8a611faa565b9350610d85565b610d828b8987612156565b93505b60008411610d8f57fe5b87841115610d9957fe5b84841415610df657600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660021790556040517fb147077378b3c0311f1c5a608d4cb621068096d16e8b1fdd1f1157ebc146306490600090a15b30849650965050506000548114610e6e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50505094509492505050565b600b6020526000908152604090205460ff1681565b60065460009082908114610f2a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b3091505b50919050565b60008054600101808255610f4661229a565b600260095460ff166002811115610f5957fe5b14610feb57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4552433230506f736974696f6e2377697468647261773a20506f736974696f6e60448201527f20686173206e6f7420796574206265656e20636c6f7365640000000000000000606482015290519081900360840190fd5b3373ffffffffffffffffffffffffffffffffffffffff8416146110d4573360009081526008602052604090205460ff1615156110d457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4552433230506f736974696f6e2377697468647261773a204f6e6c792074727560448201527f737465642077697468647261776572732063616e207769746864726177206f6e60648201527f20626568616c66206f66206f7468657273000000000000000000000000000000608482015290519081900360a40190fd5b6110de33846123b2565b91506000548114610f2e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091528120548083106111be5733600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff88168452909152812055611200565b6111ce818463ffffffff611f8b16565b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff891684529091529020555b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b600a5473ffffffffffffffffffffffffffffffffffffffff1633146112bf57600080fd5b600a5460405173ffffffffffffffffffffffffffffffffffffffff909116907ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482090600090a2600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600a5473ffffffffffffffffffffffffffffffffffffffff1681565b60076020526000908152604090205460ff1681565b600e805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f810184900484028201840190925281815292918301828280156107b95780601f1061078e576101008083540402835291602001916107b9565b336000908152600160205260408120548211156113f457600080fd5b73ffffffffffffffffffffffffffffffffffffffff8316151561141657600080fd5b33600090815260016020526040902054611436908363ffffffff611f8b16565b336000908152600160205260408082209290925573ffffffffffffffffffffffffffffffffffffffff851681522054611475908363ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350600192915050565b6009547501000000000000000000000000000000000000000000900460ff1681565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b60086020526000908152604090205460ff1681565b60095460ff1681565b33600090815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915281205461157d908363ffffffff611f9d16565b33600081815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b60065481565b600454600090819073ffffffffffffffffffffffffffffffffffffffff1633146116a657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b600080546001908101918290558060095460ff1660028111156116c557fe5b1461175757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600654869081146117ef57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b60048054600654604080517f6e0cd415000000000000000000000000000000000000000000000000000000008152938401919091525173ffffffffffffffffffffffffffffffffffffffff90911691636e0cd415916024808301926020929190829003018186803b15801561186357600080fd5b505afa158015611877573d6000803e3d6000fd5b505050506040513d602081101561188d57600080fd5b50511561194757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4552433230506f736974696f6e23696e637265617365506f736974696f6e4f6e60448201527f426568616c664f663a20506f736974696f6e206973206d617267696e2d63616c60648201527f6c65640000000000000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b6009547501000000000000000000000000000000000000000000900460ff1615611a1e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4552433230506f736974696f6e23696e637265617365506f736974696f6e4f6e60448201527f426568616c664f663a20506f736974696f6e20636c6f736564207573696e672060648201527f7472757374656420726563697069656e74000000000000000000000000000000608482015290519081900360a40190fd5b611a2786612498565b9350611a338885612577565b30945050506000548114611aa857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50509392505050565b600c5481565b600954610100900473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260036020908152604080832093909416825291909152205490565b6000611b1a612ef2565b60045460009073ffffffffffffffffffffffffffffffffffffffff163314611bc957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f4f6e6c794d617267696e236f6e6c794d617267696e3a204f6e6c79204d61726760448201527f696e2063616e2063616c6c000000000000000000000000000000000000000000606482015290519081900360840190fd5b60008054600101808255908060095460ff166002811115611be657fe5b14611c7857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4552433230506f736974696f6e236f6e6c7953746174653a20496e636f72726560448201527f6374205374617465000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60065486908114611d1057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433230506f736974696f6e236f6e6c79506f736974696f6e3a20496e636f60448201527f727265637420706f736974696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b600454600654611d369173ffffffffffffffffffffffffffffffffffffffff1690612647565b945060008560800151111515611d4857fe5b60098054602087015173ffffffffffffffffffffffffffffffffffffffff16610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909216600117919091161790556080850151611dc890612498565b600654604080519182526020820183905280519296507fc885ca8035bb269b79b1906eb7aaeeec6a4713b0976b175c25aa84b49acfa47392918290030190a1600554611e2a9073ffffffffffffffffffffffffffffffffffffffff1685612577565b30955050506000548114611e9f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5265656e7472616e637920636865636b206661696c7572650000000000000000604482015290519081900360640190fd5b50505092915050565b600a5473ffffffffffffffffffffffffffffffffffffffff163314611ecc57600080fd5b611ed5816127d5565b50565b600a5473ffffffffffffffffffffffffffffffffffffffff163314611efc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600b602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915582519384529083015280517f4f6c69ea5a0543c614b665fb438ce26ed0e5607ecdb69820ac336c815a852d6f9281900390910190a15050565b600082821115611f9757fe5b50900390565b8181018281101561082f57fe5b6000611fb4612ef2565b6004546006546000918291611fdf9173ffffffffffffffffffffffffffffffffffffffff1690612647565b925061200983610120015163ffffffff168460e0015163ffffffff16611f9d90919063ffffffff16565b42119150600083610100015163ffffffff1611801561204e575061204b8360c0015163ffffffff1684610100015163ffffffff16611f9d90919063ffffffff16565b42115b905080806120595750815b156121405773ffffffffffffffffffffffffffffffffffffffff87166000908152600b602052604090205460ff16151561214057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605260248201527f4552433230436170706564506f736974696f6e23636c6f73655573696e67547260448201527f7573746564526563697069656e743a20636c6f736572206e6f7420696e20545260648201527f55535445445f4c4154455f434c4f534552530000000000000000000000000000608482015290519081900360a40190fd5b61214b878787612885565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120548180612189868487612954565b9150915060008211801561219d5750600081115b151561223057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f4552433230506f736974696f6e23636c6f73653a2043616e6e6f7420636c6f7360448201527f65203020616d6f756e7400000000000000000000000000000000000000000000606482015290519081900360840190fd5b8581111561223a57fe5b6122448783612a58565b60408051838152905173ffffffffffffffffffffffffffffffffffffffff8916917f684222b0069d4a2e5e0d986611cc5182d543904c4e4264bf770d4e51faefc822919081900360200190a29695505050505050565b600160095460ff1660028111156122ad57fe5b148015612356575060048054600654604080517f640075f3000000000000000000000000000000000000000000000000000000008152938401919091525173ffffffffffffffffffffffffffffffffffffffff9091169163640075f3916024808301926020929190829003018186803b15801561232957600080fd5b505afa15801561233d573d6000803e3d6000fd5b505050506040513d602081101561235357600080fd5b50515b156123b057600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660021790556040517fb147077378b3c0311f1c5a608d4cb621068096d16e8b1fdd1f1157ebc146306490600090a15b565b6000806000806123c185611273565b92508215156123d35760009350611e9f565b6009546123fb90610100900473ffffffffffffffffffffffffffffffffffffffff1630612b27565b915061240a8360025484612bf5565b90506124168584612a58565b6040805184815260208101839052815173ffffffffffffffffffffffffffffffffffffffff8816927ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568928290030190a260095461248f90610100900473ffffffffffffffffffffffffffffffffffffffff168783612c1f565b95945050505050565b6000806124a483612dbe565b9050600c546124be82600254611f9d90919063ffffffff16565b111561082f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f45524332304361707065644c6f6e6723676574546f6b656e416d6f756e744f6e60448201527f4164643a20416464696e6720746f6b656e416d6f756e7420776f756c6420657860648201527f6365656420636170000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b73ffffffffffffffffffffffffffffffffffffffff8216151561259657fe5b6002546125a9908263ffffffff611f9d16565b60025573ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020546125e2908263ffffffff611f9d16565b73ffffffffffffffffffffffffffffffffffffffff831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b61264f612ef2565b612657612f56565b61265f612f75565b612667612f90565b604080517f1928b3cb00000000000000000000000000000000000000000000000000000000815260048101879052905173ffffffffffffffffffffffffffffffffffffffff881691631928b3cb91602480830192610180929190829003018186803b1580156126d557600080fd5b505afa1580156126e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061018081101561270f57600080fd5b506040805161018081018252825173ffffffffffffffffffffffffffffffffffffffff908116825260208085015182169083015283830151811692820192909252606080840151909216918101919091526080808301519082015260a0808301519082015260c08083015163ffffffff9081169183019190915260e0808401518216908301526101008084015182169083015261012080840151821690830152610140808401518216908301526101609283015116918101919091529695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811615156127f757600080fd5b600a5460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600080821161289057fe5b6009547501000000000000000000000000000000000000000000900460ff1615156128f657600980547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b6040805173ffffffffffffffffffffffffffffffffffffffff80871682526020820185905285168183015290517f2a73d58687443b7a81f739a2964d5cf5068b313c8756ae6726b20d09926f864b9181900360600190a15092915050565b60048054600654604080517f54d7986800000000000000000000000000000000000000000000000000000000815293840191909152516000928392839283928392839273ffffffffffffffffffffffffffffffffffffffff909116916354d79868916024808301926020929190829003018186803b1580156129d557600080fd5b505afa1580156129e9573d6000803e3d6000fd5b505050506040513d60208110156129ff57600080fd5b50519350612a0e898886612bf5565b9250878311612a2257828995509550612a4c565b612a2d888589612bf5565b9150888210612a3857fe5b612a43828886612bf5565b90508082955095505b50505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff82161515612a7757fe5b600254612a8a908263ffffffff611f8b16565b60025573ffffffffffffffffffffffffffffffffffffffff8216600090815260016020526040902054612ac3908263ffffffff611f8b16565b73ffffffffffffffffffffffffffffffffffffffff83166000818152600160209081526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a08231836040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612bc257600080fd5b505afa158015612bd6573d6000803e3d6000fd5b505050506040513d6020811015612bec57600080fd5b50519392505050565b6000612c1783612c0b868563ffffffff612e8016565b9063ffffffff612ea916565b949350505050565b30811580612c5857508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b15612c6257612db8565b8373ffffffffffffffffffffffffffffffffffffffff1663a9059cbb84846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b158015612d0557600080fd5b505af1158015612d19573d6000803e3d6000fd5b50505050612d25612ebe565b1515612db857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e496e746572616374237472616e736665723a205472616e7366657260448201527f206661696c656400000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b50505050565b60048054600654604080517f54d798680000000000000000000000000000000000000000000000000000000081529384019190915251600092839273ffffffffffffffffffffffffffffffffffffffff16916354d7986891602480820192602092909190829003018186803b158015612e3657600080fd5b505afa158015612e4a573d6000803e3d6000fd5b505050506040513d6020811015612e6057600080fd5b5051600254909150612e7990829063ffffffff611f8b16565b9392505050565b6000821515612e915750600061082f565b50818102818382811515612ea157fe5b041461082f57fe5b60008183811515612eb657fe5b049392505050565b6000803d8015612ed55760208114612ede57612eea565b60019150612eea565b60206000803e60005191505b501515919050565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b6080604051908101604052806004906020820280388339509192915050565b60408051808201825290600290829080388339509192915050565b60c06040519081016040528060069060208202803883395091929150505600a165627a7a72305820febb5ce4d6cb0de254a2396f5755d91f4ff803dba5cc2b40eda73a2b701e45a00029

Swarm Source

bzzr://febb5ce4d6cb0de254a2396f5755d91f4ff803dba5cc2b40eda73a2b701e45a0
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.