ETH Price: $2,898.50 (-4.00%)
Gas: 11 Gwei

Transaction Decoder

Block:
13967057 at Jan-08-2022 08:53:45 PM +UTC
Transaction Fee:
0.0096351 ETH $27.93
Gas Used:
32,117 Gas / 300 Gwei

Account State Difference:

  Address   Before After State Difference Code
(EzilPool 3)
292.233711479137202156 Eth292.237822235029220067 Eth0.004110755892017911
0xF7411AED...223371d52
1.583069438061749018 Eth
Nonce: 21
1.573434338061749018 Eth
Nonce: 22
0.0096351

Execution Trace

Proxy.a9059cbb( )
  • Synthetix.setMessageSender( sender=0xF7411AED9bF7aA16C2D75F6263Da9f6223371d52 )
    File 1 of 2: Proxy
    /* 
     * Havven Token Contract Proxy
     * ========================
     * 
     * This contract points to an underlying target which implements its
     * actual functionality, while allowing that functionality to be upgraded.
     */
    
    pragma solidity 0.4.24;
    
    /**
     * @title A contract with an owner.
     * @notice Contract ownership can be transferred by first nominating the new owner,
     * who must then accept the ownership, which prevents accidental incorrect ownership transfers.
     */
    contract Owned {
        address public owner;
        address public nominatedOwner;
    
        /**
         * @dev Owned Constructor
         */
        constructor(address _owner)
            public
        {
            require(_owner != address(0));
            owner = _owner;
            emit OwnerChanged(address(0), _owner);
        }
    
        /**
         * @notice Nominate a new owner of this contract.
         * @dev Only the current owner may nominate a new owner.
         */
        function nominateNewOwner(address _owner)
            external
            onlyOwner
        {
            nominatedOwner = _owner;
            emit OwnerNominated(_owner);
        }
    
        /**
         * @notice Accept the nomination to be owner.
         */
        function acceptOwnership()
            external
        {
            require(msg.sender == nominatedOwner);
            emit OwnerChanged(owner, nominatedOwner);
            owner = nominatedOwner;
            nominatedOwner = address(0);
        }
    
        modifier onlyOwner
        {
            require(msg.sender == owner);
            _;
        }
    
        event OwnerNominated(address newOwner);
        event OwnerChanged(address oldOwner, address newOwner);
    }
    
    // This contract should be treated like an abstract contract
    contract Proxyable is Owned {
        /* The proxy this contract exists behind. */
        Proxy public proxy;
    
        /* The caller of the proxy, passed through to this contract.
         * Note that every function using this member must apply the onlyProxy or
         * optionalProxy modifiers, otherwise their invocations can use stale values. */ 
        address messageSender; 
    
        constructor(address _proxy, address _owner)
            Owned(_owner)
            public
        {
            proxy = Proxy(_proxy);
            emit ProxyUpdated(_proxy);
        }
    
        function setProxy(address _proxy)
            external
            onlyOwner
        {
            proxy = Proxy(_proxy);
            emit ProxyUpdated(_proxy);
        }
    
        function setMessageSender(address sender)
            external
            onlyProxy
        {
            messageSender = sender;
        }
    
        modifier onlyProxy {
            require(Proxy(msg.sender) == proxy);
            _;
        }
    
        modifier optionalProxy
        {
            if (Proxy(msg.sender) != proxy) {
                messageSender = msg.sender;
            }
            _;
        }
    
        modifier optionalProxy_onlyOwner
        {
            if (Proxy(msg.sender) != proxy) {
                messageSender = msg.sender;
            }
            require(messageSender == owner);
            _;
        }
    
        event ProxyUpdated(address proxyAddress);
    }
    
    contract Proxy is Owned {
    
        Proxyable public target;
        bool public useDELEGATECALL;
    
        constructor(address _owner)
            Owned(_owner)
            public
        {}
    
        function setTarget(Proxyable _target)
            external
            onlyOwner
        {
            target = _target;
            emit TargetUpdated(_target);
        }
    
        function setUseDELEGATECALL(bool value) 
            external
            onlyOwner
        {
            useDELEGATECALL = value;
        }
    
        function _emit(bytes callData, uint numTopics,
                       bytes32 topic1, bytes32 topic2,
                       bytes32 topic3, bytes32 topic4)
            external
            onlyTarget
        {
            uint size = callData.length;
            bytes memory _callData = callData;
    
            assembly {
                /* The first 32 bytes of callData contain its length (as specified by the abi). 
                 * Length is assumed to be a uint256 and therefore maximum of 32 bytes
                 * in length. It is also leftpadded to be a multiple of 32 bytes.
                 * This means moving call_data across 32 bytes guarantees we correctly access
                 * the data itself. */
                switch numTopics
                case 0 {
                    log0(add(_callData, 32), size)
                } 
                case 1 {
                    log1(add(_callData, 32), size, topic1)
                }
                case 2 {
                    log2(add(_callData, 32), size, topic1, topic2)
                }
                case 3 {
                    log3(add(_callData, 32), size, topic1, topic2, topic3)
                }
                case 4 {
                    log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
                }
            }
        }
    
        function()
            external
            payable
        {
            if (useDELEGATECALL) {
                assembly {
                    /* Copy call data into free memory region. */
                    let free_ptr := mload(0x40)
                    calldatacopy(free_ptr, 0, calldatasize)
    
                    /* Forward all gas and call data to the target contract. */
                    let result := delegatecall(gas, sload(target_slot), free_ptr, calldatasize, 0, 0)
                    returndatacopy(free_ptr, 0, returndatasize)
    
                    /* Revert if the call failed, otherwise return the result. */
                    if iszero(result) { revert(free_ptr, returndatasize) }
                    return(free_ptr, returndatasize)
                }
            } else {
                /* Here we are as above, but must send the messageSender explicitly 
                 * since we are using CALL rather than DELEGATECALL. */
                target.setMessageSender(msg.sender);
                assembly {
                    let free_ptr := mload(0x40)
                    calldatacopy(free_ptr, 0, calldatasize)
    
                    /* We must explicitly forward ether to the underlying contract as well. */
                    let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
                    returndatacopy(free_ptr, 0, returndatasize)
    
                    if iszero(result) { revert(free_ptr, returndatasize) }
                    return(free_ptr, returndatasize)
                }
            }
        }
    
        modifier onlyTarget {
            require(Proxyable(msg.sender) == target);
            _;
        }
    
        event TargetUpdated(Proxyable newTarget);
    }

    File 2 of 2: Synthetix
    /*
    
    ⚠⚠⚠ WARNING WARNING WARNING ⚠⚠⚠
    
    This is a TARGET contract - DO NOT CONNECT TO IT DIRECTLY IN YOUR CONTRACTS or DAPPS!
    
    This contract has an associated PROXY that MUST be used for all integrations - this TARGET will be REPLACED in an upcoming Synthetix release!
    The proxy for this contract can be found here:
    
    https://contracts.synthetix.io/ProxyERC20
    
    *//*
       ____            __   __        __   _
      / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
     _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
    /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
         /___/
    
    * Synthetix: Synthetix.sol
    *
    * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/Synthetix.sol
    * Docs: https://docs.synthetix.io/contracts/Synthetix
    *
    * Contract Dependencies: 
    *	- BaseSynthetix
    *	- ExternStateToken
    *	- IAddressResolver
    *	- IERC20
    *	- ISynthetix
    *	- MixinResolver
    *	- Owned
    *	- Proxyable
    *	- State
    * Libraries: 
    *	- SafeDecimalMath
    *	- SafeMath
    *	- VestingEntries
    *
    * MIT License
    * ===========
    *
    * Copyright (c) 2021 Synthetix
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    */
    
    
    
    pragma solidity >=0.4.24;
    
    // https://docs.synthetix.io/contracts/source/interfaces/ierc20
    interface IERC20 {
        // ERC20 Optional Views
        function name() external view returns (string memory);
    
        function symbol() external view returns (string memory);
    
        function decimals() external view returns (uint8);
    
        // Views
        function totalSupply() external view returns (uint);
    
        function balanceOf(address owner) external view returns (uint);
    
        function allowance(address owner, address spender) external view returns (uint);
    
        // Mutative functions
        function transfer(address to, uint value) external returns (bool);
    
        function approve(address spender, uint value) external returns (bool);
    
        function transferFrom(
            address from,
            address to,
            uint value
        ) external returns (bool);
    
        // Events
        event Transfer(address indexed from, address indexed to, uint value);
    
        event Approval(address indexed owner, address indexed spender, uint value);
    }
    
    
    // https://docs.synthetix.io/contracts/source/contracts/owned
    contract Owned {
        address public owner;
        address public nominatedOwner;
    
        constructor(address _owner) public {
            require(_owner != address(0), "Owner address cannot be 0");
            owner = _owner;
            emit OwnerChanged(address(0), _owner);
        }
    
        function nominateNewOwner(address _owner) external onlyOwner {
            nominatedOwner = _owner;
            emit OwnerNominated(_owner);
        }
    
        function acceptOwnership() external {
            require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
            emit OwnerChanged(owner, nominatedOwner);
            owner = nominatedOwner;
            nominatedOwner = address(0);
        }
    
        modifier onlyOwner {
            _onlyOwner();
            _;
        }
    
        function _onlyOwner() private view {
            require(msg.sender == owner, "Only the contract owner may perform this action");
        }
    
        event OwnerNominated(address newOwner);
        event OwnerChanged(address oldOwner, address newOwner);
    }
    
    
    // Inheritance
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/proxy
    contract Proxy is Owned {
        Proxyable public target;
    
        constructor(address _owner) public Owned(_owner) {}
    
        function setTarget(Proxyable _target) external onlyOwner {
            target = _target;
            emit TargetUpdated(_target);
        }
    
        function _emit(
            bytes calldata callData,
            uint numTopics,
            bytes32 topic1,
            bytes32 topic2,
            bytes32 topic3,
            bytes32 topic4
        ) external onlyTarget {
            uint size = callData.length;
            bytes memory _callData = callData;
    
            assembly {
                /* The first 32 bytes of callData contain its length (as specified by the abi).
                 * Length is assumed to be a uint256 and therefore maximum of 32 bytes
                 * in length. It is also leftpadded to be a multiple of 32 bytes.
                 * This means moving call_data across 32 bytes guarantees we correctly access
                 * the data itself. */
                switch numTopics
                    case 0 {
                        log0(add(_callData, 32), size)
                    }
                    case 1 {
                        log1(add(_callData, 32), size, topic1)
                    }
                    case 2 {
                        log2(add(_callData, 32), size, topic1, topic2)
                    }
                    case 3 {
                        log3(add(_callData, 32), size, topic1, topic2, topic3)
                    }
                    case 4 {
                        log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
                    }
            }
        }
    
        // solhint-disable no-complex-fallback
        function() external payable {
            // Mutable call setting Proxyable.messageSender as this is using call not delegatecall
            target.setMessageSender(msg.sender);
    
            assembly {
                let free_ptr := mload(0x40)
                calldatacopy(free_ptr, 0, calldatasize)
    
                /* We must explicitly forward ether to the underlying contract as well. */
                let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
                returndatacopy(free_ptr, 0, returndatasize)
    
                if iszero(result) {
                    revert(free_ptr, returndatasize)
                }
                return(free_ptr, returndatasize)
            }
        }
    
        modifier onlyTarget {
            require(Proxyable(msg.sender) == target, "Must be proxy target");
            _;
        }
    
        event TargetUpdated(Proxyable newTarget);
    }
    
    
    // Inheritance
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/proxyable
    contract Proxyable is Owned {
        // This contract should be treated like an abstract contract
    
        /* The proxy this contract exists behind. */
        Proxy public proxy;
        Proxy public integrationProxy;
    
        /* The caller of the proxy, passed through to this contract.
         * Note that every function using this member must apply the onlyProxy or
         * optionalProxy modifiers, otherwise their invocations can use stale values. */
        address public messageSender;
    
        constructor(address payable _proxy) internal {
            // This contract is abstract, and thus cannot be instantiated directly
            require(owner != address(0), "Owner must be set");
    
            proxy = Proxy(_proxy);
            emit ProxyUpdated(_proxy);
        }
    
        function setProxy(address payable _proxy) external onlyOwner {
            proxy = Proxy(_proxy);
            emit ProxyUpdated(_proxy);
        }
    
        function setIntegrationProxy(address payable _integrationProxy) external onlyOwner {
            integrationProxy = Proxy(_integrationProxy);
        }
    
        function setMessageSender(address sender) external onlyProxy {
            messageSender = sender;
        }
    
        modifier onlyProxy {
            _onlyProxy();
            _;
        }
    
        function _onlyProxy() private view {
            require(Proxy(msg.sender) == proxy || Proxy(msg.sender) == integrationProxy, "Only the proxy can call");
        }
    
        modifier optionalProxy {
            _optionalProxy();
            _;
        }
    
        function _optionalProxy() private {
            if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
                messageSender = msg.sender;
            }
        }
    
        modifier optionalProxy_onlyOwner {
            _optionalProxy_onlyOwner();
            _;
        }
    
        // solhint-disable-next-line func-name-mixedcase
        function _optionalProxy_onlyOwner() private {
            if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
                messageSender = msg.sender;
            }
            require(messageSender == owner, "Owner only function");
        }
    
        event ProxyUpdated(address proxyAddress);
    }
    
    
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "SafeMath: subtraction overflow");
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0, "SafeMath: division by zero");
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0, "SafeMath: modulo by zero");
            return a % b;
        }
    }
    
    
    // Libraries
    
    
    // https://docs.synthetix.io/contracts/source/libraries/safedecimalmath
    library SafeDecimalMath {
        using SafeMath for uint;
    
        /* Number of decimal places in the representations. */
        uint8 public constant decimals = 18;
        uint8 public constant highPrecisionDecimals = 27;
    
        /* The number representing 1.0. */
        uint public constant UNIT = 10**uint(decimals);
    
        /* The number representing 1.0 for higher fidelity numbers. */
        uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals);
        uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals);
    
        /**
         * @return Provides an interface to UNIT.
         */
        function unit() external pure returns (uint) {
            return UNIT;
        }
    
        /**
         * @return Provides an interface to PRECISE_UNIT.
         */
        function preciseUnit() external pure returns (uint) {
            return PRECISE_UNIT;
        }
    
        /**
         * @return The result of multiplying x and y, interpreting the operands as fixed-point
         * decimals.
         *
         * @dev A unit factor is divided out after the product of x and y is evaluated,
         * so that product must be less than 2**256. As this is an integer division,
         * the internal division always rounds down. This helps save on gas. Rounding
         * is more expensive on gas.
         */
        function multiplyDecimal(uint x, uint y) internal pure returns (uint) {
            /* Divide by UNIT to remove the extra factor introduced by the product. */
            return x.mul(y) / UNIT;
        }
    
        /**
         * @return The result of safely multiplying x and y, interpreting the operands
         * as fixed-point decimals of the specified precision unit.
         *
         * @dev The operands should be in the form of a the specified unit factor which will be
         * divided out after the product of x and y is evaluated, so that product must be
         * less than 2**256.
         *
         * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
         * Rounding is useful when you need to retain fidelity for small decimal numbers
         * (eg. small fractions or percentages).
         */
        function _multiplyDecimalRound(
            uint x,
            uint y,
            uint precisionUnit
        ) private pure returns (uint) {
            /* Divide by UNIT to remove the extra factor introduced by the product. */
            uint quotientTimesTen = x.mul(y) / (precisionUnit / 10);
    
            if (quotientTimesTen % 10 >= 5) {
                quotientTimesTen += 10;
            }
    
            return quotientTimesTen / 10;
        }
    
        /**
         * @return The result of safely multiplying x and y, interpreting the operands
         * as fixed-point decimals of a precise unit.
         *
         * @dev The operands should be in the precise unit factor which will be
         * divided out after the product of x and y is evaluated, so that product must be
         * less than 2**256.
         *
         * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
         * Rounding is useful when you need to retain fidelity for small decimal numbers
         * (eg. small fractions or percentages).
         */
        function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
            return _multiplyDecimalRound(x, y, PRECISE_UNIT);
        }
    
        /**
         * @return The result of safely multiplying x and y, interpreting the operands
         * as fixed-point decimals of a standard unit.
         *
         * @dev The operands should be in the standard unit factor which will be
         * divided out after the product of x and y is evaluated, so that product must be
         * less than 2**256.
         *
         * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
         * Rounding is useful when you need to retain fidelity for small decimal numbers
         * (eg. small fractions or percentages).
         */
        function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) {
            return _multiplyDecimalRound(x, y, UNIT);
        }
    
        /**
         * @return The result of safely dividing x and y. The return value is a high
         * precision decimal.
         *
         * @dev y is divided after the product of x and the standard precision unit
         * is evaluated, so the product of x and UNIT must be less than 2**256. As
         * this is an integer division, the result is always rounded down.
         * This helps save on gas. Rounding is more expensive on gas.
         */
        function divideDecimal(uint x, uint y) internal pure returns (uint) {
            /* Reintroduce the UNIT factor that will be divided out by y. */
            return x.mul(UNIT).div(y);
        }
    
        /**
         * @return The result of safely dividing x and y. The return value is as a rounded
         * decimal in the precision unit specified in the parameter.
         *
         * @dev y is divided after the product of x and the specified precision unit
         * is evaluated, so the product of x and the specified precision unit must
         * be less than 2**256. The result is rounded to the nearest increment.
         */
        function _divideDecimalRound(
            uint x,
            uint y,
            uint precisionUnit
        ) private pure returns (uint) {
            uint resultTimesTen = x.mul(precisionUnit * 10).div(y);
    
            if (resultTimesTen % 10 >= 5) {
                resultTimesTen += 10;
            }
    
            return resultTimesTen / 10;
        }
    
        /**
         * @return The result of safely dividing x and y. The return value is as a rounded
         * standard precision decimal.
         *
         * @dev y is divided after the product of x and the standard precision unit
         * is evaluated, so the product of x and the standard precision unit must
         * be less than 2**256. The result is rounded to the nearest increment.
         */
        function divideDecimalRound(uint x, uint y) internal pure returns (uint) {
            return _divideDecimalRound(x, y, UNIT);
        }
    
        /**
         * @return The result of safely dividing x and y. The return value is as a rounded
         * high precision decimal.
         *
         * @dev y is divided after the product of x and the high precision unit
         * is evaluated, so the product of x and the high precision unit must
         * be less than 2**256. The result is rounded to the nearest increment.
         */
        function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
            return _divideDecimalRound(x, y, PRECISE_UNIT);
        }
    
        /**
         * @dev Convert a standard decimal representation to a high precision one.
         */
        function decimalToPreciseDecimal(uint i) internal pure returns (uint) {
            return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
        }
    
        /**
         * @dev Convert a high precision decimal to a standard decimal representation.
         */
        function preciseDecimalToDecimal(uint i) internal pure returns (uint) {
            uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);
    
            if (quotientTimesTen % 10 >= 5) {
                quotientTimesTen += 10;
            }
    
            return quotientTimesTen / 10;
        }
    
        // Computes `a - b`, setting the value to 0 if b > a.
        function floorsub(uint a, uint b) internal pure returns (uint) {
            return b >= a ? 0 : a - b;
        }
    }
    
    
    // Inheritance
    
    
    // https://docs.synthetix.io/contracts/source/contracts/state
    contract State is Owned {
        // the address of the contract that can modify variables
        // this can only be changed by the owner of this contract
        address public associatedContract;
    
        constructor(address _associatedContract) internal {
            // This contract is abstract, and thus cannot be instantiated directly
            require(owner != address(0), "Owner must be set");
    
            associatedContract = _associatedContract;
            emit AssociatedContractUpdated(_associatedContract);
        }
    
        /* ========== SETTERS ========== */
    
        // Change the associated contract to a new address
        function setAssociatedContract(address _associatedContract) external onlyOwner {
            associatedContract = _associatedContract;
            emit AssociatedContractUpdated(_associatedContract);
        }
    
        /* ========== MODIFIERS ========== */
    
        modifier onlyAssociatedContract {
            require(msg.sender == associatedContract, "Only the associated contract can perform this action");
            _;
        }
    
        /* ========== EVENTS ========== */
    
        event AssociatedContractUpdated(address associatedContract);
    }
    
    
    // Inheritance
    
    
    // https://docs.synthetix.io/contracts/source/contracts/tokenstate
    contract TokenState is Owned, State {
        /* ERC20 fields. */
        mapping(address => uint) public balanceOf;
        mapping(address => mapping(address => uint)) public allowance;
    
        constructor(address _owner, address _associatedContract) public Owned(_owner) State(_associatedContract) {}
    
        /* ========== SETTERS ========== */
    
        /**
         * @notice Set ERC20 allowance.
         * @dev Only the associated contract may call this.
         * @param tokenOwner The authorising party.
         * @param spender The authorised party.
         * @param value The total value the authorised party may spend on the
         * authorising party's behalf.
         */
        function setAllowance(
            address tokenOwner,
            address spender,
            uint value
        ) external onlyAssociatedContract {
            allowance[tokenOwner][spender] = value;
        }
    
        /**
         * @notice Set the balance in a given account
         * @dev Only the associated contract may call this.
         * @param account The account whose value to set.
         * @param value The new balance of the given account.
         */
        function setBalanceOf(address account, uint value) external onlyAssociatedContract {
            balanceOf[account] = value;
        }
    }
    
    
    // Inheritance
    
    
    // Libraries
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/externstatetoken
    contract ExternStateToken is Owned, Proxyable {
        using SafeMath for uint;
        using SafeDecimalMath for uint;
    
        /* ========== STATE VARIABLES ========== */
    
        /* Stores balances and allowances. */
        TokenState public tokenState;
    
        /* Other ERC20 fields. */
        string public name;
        string public symbol;
        uint public totalSupply;
        uint8 public decimals;
    
        constructor(
            address payable _proxy,
            TokenState _tokenState,
            string memory _name,
            string memory _symbol,
            uint _totalSupply,
            uint8 _decimals,
            address _owner
        ) public Owned(_owner) Proxyable(_proxy) {
            tokenState = _tokenState;
    
            name = _name;
            symbol = _symbol;
            totalSupply = _totalSupply;
            decimals = _decimals;
        }
    
        /* ========== VIEWS ========== */
    
        /**
         * @notice Returns the ERC20 allowance of one party to spend on behalf of another.
         * @param owner The party authorising spending of their funds.
         * @param spender The party spending tokenOwner's funds.
         */
        function allowance(address owner, address spender) public view returns (uint) {
            return tokenState.allowance(owner, spender);
        }
    
        /**
         * @notice Returns the ERC20 token balance of a given account.
         */
        function balanceOf(address account) external view returns (uint) {
            return tokenState.balanceOf(account);
        }
    
        /* ========== MUTATIVE FUNCTIONS ========== */
    
        /**
         * @notice Set the address of the TokenState contract.
         * @dev This can be used to "pause" transfer functionality, by pointing the tokenState at 0x000..
         * as balances would be unreachable.
         */
        function setTokenState(TokenState _tokenState) external optionalProxy_onlyOwner {
            tokenState = _tokenState;
            emitTokenStateUpdated(address(_tokenState));
        }
    
        function _internalTransfer(
            address from,
            address to,
            uint value
        ) internal returns (bool) {
            /* Disallow transfers to irretrievable-addresses. */
            require(to != address(0) && to != address(this) && to != address(proxy), "Cannot transfer to this address");
    
            // Insufficient balance will be handled by the safe subtraction.
            tokenState.setBalanceOf(from, tokenState.balanceOf(from).sub(value));
            tokenState.setBalanceOf(to, tokenState.balanceOf(to).add(value));
    
            // Emit a standard ERC20 transfer event
            emitTransfer(from, to, value);
    
            return true;
        }
    
        /**
         * @dev Perform an ERC20 token transfer. Designed to be called by transfer functions possessing
         * the onlyProxy or optionalProxy modifiers.
         */
        function _transferByProxy(
            address from,
            address to,
            uint value
        ) internal returns (bool) {
            return _internalTransfer(from, to, value);
        }
    
        /*
         * @dev Perform an ERC20 token transferFrom. Designed to be called by transferFrom functions
         * possessing the optionalProxy or optionalProxy modifiers.
         */
        function _transferFromByProxy(
            address sender,
            address from,
            address to,
            uint value
        ) internal returns (bool) {
            /* Insufficient allowance will be handled by the safe subtraction. */
            tokenState.setAllowance(from, sender, tokenState.allowance(from, sender).sub(value));
            return _internalTransfer(from, to, value);
        }
    
        /**
         * @notice Approves spender to transfer on the message sender's behalf.
         */
        function approve(address spender, uint value) public optionalProxy returns (bool) {
            address sender = messageSender;
    
            tokenState.setAllowance(sender, spender, value);
            emitApproval(sender, spender, value);
            return true;
        }
    
        /* ========== EVENTS ========== */
        function addressToBytes32(address input) internal pure returns (bytes32) {
            return bytes32(uint256(uint160(input)));
        }
    
        event Transfer(address indexed from, address indexed to, uint value);
        bytes32 internal constant TRANSFER_SIG = keccak256("Transfer(address,address,uint256)");
    
        function emitTransfer(
            address from,
            address to,
            uint value
        ) internal {
            proxy._emit(abi.encode(value), 3, TRANSFER_SIG, addressToBytes32(from), addressToBytes32(to), 0);
        }
    
        event Approval(address indexed owner, address indexed spender, uint value);
        bytes32 internal constant APPROVAL_SIG = keccak256("Approval(address,address,uint256)");
    
        function emitApproval(
            address owner,
            address spender,
            uint value
        ) internal {
            proxy._emit(abi.encode(value), 3, APPROVAL_SIG, addressToBytes32(owner), addressToBytes32(spender), 0);
        }
    
        event TokenStateUpdated(address newTokenState);
        bytes32 internal constant TOKENSTATEUPDATED_SIG = keccak256("TokenStateUpdated(address)");
    
        function emitTokenStateUpdated(address newTokenState) internal {
            proxy._emit(abi.encode(newTokenState), 1, TOKENSTATEUPDATED_SIG, 0, 0, 0);
        }
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver
    interface IAddressResolver {
        function getAddress(bytes32 name) external view returns (address);
    
        function getSynth(bytes32 key) external view returns (address);
    
        function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address);
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/isynth
    interface ISynth {
        // Views
        function currencyKey() external view returns (bytes32);
    
        function transferableSynths(address account) external view returns (uint);
    
        // Mutative functions
        function transferAndSettle(address to, uint value) external returns (bool);
    
        function transferFromAndSettle(
            address from,
            address to,
            uint value
        ) external returns (bool);
    
        // Restricted: used internally to Synthetix
        function burn(address account, uint amount) external;
    
        function issue(address account, uint amount) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/iissuer
    interface IIssuer {
        // Views
        function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid);
    
        function availableCurrencyKeys() external view returns (bytes32[] memory);
    
        function availableSynthCount() external view returns (uint);
    
        function availableSynths(uint index) external view returns (ISynth);
    
        function canBurnSynths(address account) external view returns (bool);
    
        function collateral(address account) external view returns (uint);
    
        function collateralisationRatio(address issuer) external view returns (uint);
    
        function collateralisationRatioAndAnyRatesInvalid(address _issuer)
            external
            view
            returns (uint cratio, bool anyRateIsInvalid);
    
        function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint debtBalance);
    
        function issuanceRatio() external view returns (uint);
    
        function lastIssueEvent(address account) external view returns (uint);
    
        function maxIssuableSynths(address issuer) external view returns (uint maxIssuable);
    
        function minimumStakeTime() external view returns (uint);
    
        function remainingIssuableSynths(address issuer)
            external
            view
            returns (
                uint maxIssuable,
                uint alreadyIssued,
                uint totalSystemDebt
            );
    
        function synths(bytes32 currencyKey) external view returns (ISynth);
    
        function getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory);
    
        function synthsByAddress(address synthAddress) external view returns (bytes32);
    
        function totalIssuedSynths(bytes32 currencyKey, bool excludeOtherCollateral) external view returns (uint);
    
        function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance)
            external
            view
            returns (uint transferable, bool anyRateIsInvalid);
    
        // Restricted: used internally to Synthetix
        function issueSynths(address from, uint amount) external;
    
        function issueSynthsOnBehalf(
            address issueFor,
            address from,
            uint amount
        ) external;
    
        function issueMaxSynths(address from) external;
    
        function issueMaxSynthsOnBehalf(address issueFor, address from) external;
    
        function burnSynths(address from, uint amount) external;
    
        function burnSynthsOnBehalf(
            address burnForAddress,
            address from,
            uint amount
        ) external;
    
        function burnSynthsToTarget(address from) external;
    
        function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external;
    
        function burnForRedemption(
            address deprecatedSynthProxy,
            address account,
            uint balance
        ) external;
    
        function liquidateDelinquentAccount(
            address account,
            uint susdAmount,
            address liquidator
        ) external returns (uint totalRedeemed, uint amountToLiquidate);
    }
    
    
    // Inheritance
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/addressresolver
    contract AddressResolver is Owned, IAddressResolver {
        mapping(bytes32 => address) public repository;
    
        constructor(address _owner) public Owned(_owner) {}
    
        /* ========== RESTRICTED FUNCTIONS ========== */
    
        function importAddresses(bytes32[] calldata names, address[] calldata destinations) external onlyOwner {
            require(names.length == destinations.length, "Input lengths must match");
    
            for (uint i = 0; i < names.length; i++) {
                bytes32 name = names[i];
                address destination = destinations[i];
                repository[name] = destination;
                emit AddressImported(name, destination);
            }
        }
    
        /* ========= PUBLIC FUNCTIONS ========== */
    
        function rebuildCaches(MixinResolver[] calldata destinations) external {
            for (uint i = 0; i < destinations.length; i++) {
                destinations[i].rebuildCache();
            }
        }
    
        /* ========== VIEWS ========== */
    
        function areAddressesImported(bytes32[] calldata names, address[] calldata destinations) external view returns (bool) {
            for (uint i = 0; i < names.length; i++) {
                if (repository[names[i]] != destinations[i]) {
                    return false;
                }
            }
            return true;
        }
    
        function getAddress(bytes32 name) external view returns (address) {
            return repository[name];
        }
    
        function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address) {
            address _foundAddress = repository[name];
            require(_foundAddress != address(0), reason);
            return _foundAddress;
        }
    
        function getSynth(bytes32 key) external view returns (address) {
            IIssuer issuer = IIssuer(repository["Issuer"]);
            require(address(issuer) != address(0), "Cannot find Issuer address");
            return address(issuer.synths(key));
        }
    
        /* ========== EVENTS ========== */
    
        event AddressImported(bytes32 name, address destination);
    }
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/mixinresolver
    contract MixinResolver {
        AddressResolver public resolver;
    
        mapping(bytes32 => address) private addressCache;
    
        constructor(address _resolver) internal {
            resolver = AddressResolver(_resolver);
        }
    
        /* ========== INTERNAL FUNCTIONS ========== */
    
        function combineArrays(bytes32[] memory first, bytes32[] memory second)
            internal
            pure
            returns (bytes32[] memory combination)
        {
            combination = new bytes32[](first.length + second.length);
    
            for (uint i = 0; i < first.length; i++) {
                combination[i] = first[i];
            }
    
            for (uint j = 0; j < second.length; j++) {
                combination[first.length + j] = second[j];
            }
        }
    
        /* ========== PUBLIC FUNCTIONS ========== */
    
        // Note: this function is public not external in order for it to be overridden and invoked via super in subclasses
        function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {}
    
        function rebuildCache() public {
            bytes32[] memory requiredAddresses = resolverAddressesRequired();
            // The resolver must call this function whenver it updates its state
            for (uint i = 0; i < requiredAddresses.length; i++) {
                bytes32 name = requiredAddresses[i];
                // Note: can only be invoked once the resolver has all the targets needed added
                address destination =
                    resolver.requireAndGetAddress(name, string(abi.encodePacked("Resolver missing target: ", name)));
                addressCache[name] = destination;
                emit CacheUpdated(name, destination);
            }
        }
    
        /* ========== VIEWS ========== */
    
        function isResolverCached() external view returns (bool) {
            bytes32[] memory requiredAddresses = resolverAddressesRequired();
            for (uint i = 0; i < requiredAddresses.length; i++) {
                bytes32 name = requiredAddresses[i];
                // false if our cache is invalid or if the resolver doesn't have the required address
                if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
                    return false;
                }
            }
    
            return true;
        }
    
        /* ========== INTERNAL FUNCTIONS ========== */
    
        function requireAndGetAddress(bytes32 name) internal view returns (address) {
            address _foundAddress = addressCache[name];
            require(_foundAddress != address(0), string(abi.encodePacked("Missing address: ", name)));
            return _foundAddress;
        }
    
        /* ========== EVENTS ========== */
    
        event CacheUpdated(bytes32 name, address destination);
    }
    
    
    interface IVirtualSynth {
        // Views
        function balanceOfUnderlying(address account) external view returns (uint);
    
        function rate() external view returns (uint);
    
        function readyToSettle() external view returns (bool);
    
        function secsLeftInWaitingPeriod() external view returns (uint);
    
        function settled() external view returns (bool);
    
        function synth() external view returns (ISynth);
    
        // Mutative functions
        function settle(address account) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/isynthetix
    interface ISynthetix {
        // Views
        function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid);
    
        function availableCurrencyKeys() external view returns (bytes32[] memory);
    
        function availableSynthCount() external view returns (uint);
    
        function availableSynths(uint index) external view returns (ISynth);
    
        function collateral(address account) external view returns (uint);
    
        function collateralisationRatio(address issuer) external view returns (uint);
    
        function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint);
    
        function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);
    
        function maxIssuableSynths(address issuer) external view returns (uint maxIssuable);
    
        function remainingIssuableSynths(address issuer)
            external
            view
            returns (
                uint maxIssuable,
                uint alreadyIssued,
                uint totalSystemDebt
            );
    
        function synths(bytes32 currencyKey) external view returns (ISynth);
    
        function synthsByAddress(address synthAddress) external view returns (bytes32);
    
        function totalIssuedSynths(bytes32 currencyKey) external view returns (uint);
    
        function totalIssuedSynthsExcludeOtherCollateral(bytes32 currencyKey) external view returns (uint);
    
        function transferableSynthetix(address account) external view returns (uint transferable);
    
        // Mutative Functions
        function burnSynths(uint amount) external;
    
        function burnSynthsOnBehalf(address burnForAddress, uint amount) external;
    
        function burnSynthsToTarget() external;
    
        function burnSynthsToTargetOnBehalf(address burnForAddress) external;
    
        function exchange(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey
        ) external returns (uint amountReceived);
    
        function exchangeOnBehalf(
            address exchangeForAddress,
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey
        ) external returns (uint amountReceived);
    
        function exchangeWithTracking(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external returns (uint amountReceived);
    
        function exchangeWithTrackingForInitiator(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external returns (uint amountReceived);
    
        function exchangeOnBehalfWithTracking(
            address exchangeForAddress,
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external returns (uint amountReceived);
    
        function exchangeWithVirtual(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            bytes32 trackingCode
        ) external returns (uint amountReceived, IVirtualSynth vSynth);
    
        function issueMaxSynths() external;
    
        function issueMaxSynthsOnBehalf(address issueForAddress) external;
    
        function issueSynths(uint amount) external;
    
        function issueSynthsOnBehalf(address issueForAddress, uint amount) external;
    
        function mint() external returns (bool);
    
        function settle(bytes32 currencyKey)
            external
            returns (
                uint reclaimed,
                uint refunded,
                uint numEntries
            );
    
        // Liquidations
        function liquidateDelinquentAccount(address account, uint susdAmount) external returns (bool);
    
        // Restricted Functions
    
        function mintSecondary(address account, uint amount) external;
    
        function mintSecondaryRewards(uint amount) external;
    
        function burnSecondary(address account, uint amount) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/isynthetixstate
    interface ISynthetixState {
        // Views
        function debtLedger(uint index) external view returns (uint);
    
        function issuanceData(address account) external view returns (uint initialDebtOwnership, uint debtEntryIndex);
    
        function debtLedgerLength() external view returns (uint);
    
        function hasIssued(address account) external view returns (bool);
    
        function lastDebtLedgerEntry() external view returns (uint);
    
        // Mutative functions
        function incrementTotalIssuerCount() external;
    
        function decrementTotalIssuerCount() external;
    
        function setCurrentIssuanceData(address account, uint initialDebtOwnership) external;
    
        function appendDebtLedgerValue(uint value) external;
    
        function clearIssuanceData(address account) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/isystemstatus
    interface ISystemStatus {
        struct Status {
            bool canSuspend;
            bool canResume;
        }
    
        struct Suspension {
            bool suspended;
            // reason is an integer code,
            // 0 => no reason, 1 => upgrading, 2+ => defined by system usage
            uint248 reason;
        }
    
        // Views
        function accessControl(bytes32 section, address account) external view returns (bool canSuspend, bool canResume);
    
        function requireSystemActive() external view;
    
        function requireIssuanceActive() external view;
    
        function requireExchangeActive() external view;
    
        function requireExchangeBetweenSynthsAllowed(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
    
        function requireSynthActive(bytes32 currencyKey) external view;
    
        function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
    
        function systemSuspension() external view returns (bool suspended, uint248 reason);
    
        function issuanceSuspension() external view returns (bool suspended, uint248 reason);
    
        function exchangeSuspension() external view returns (bool suspended, uint248 reason);
    
        function synthExchangeSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);
    
        function synthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);
    
        function getSynthExchangeSuspensions(bytes32[] calldata synths)
            external
            view
            returns (bool[] memory exchangeSuspensions, uint256[] memory reasons);
    
        function getSynthSuspensions(bytes32[] calldata synths)
            external
            view
            returns (bool[] memory suspensions, uint256[] memory reasons);
    
        // Restricted functions
        function suspendSynth(bytes32 currencyKey, uint256 reason) external;
    
        function updateAccessControl(
            bytes32 section,
            address account,
            bool canSuspend,
            bool canResume
        ) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/iexchanger
    interface IExchanger {
        // Views
        function calculateAmountAfterSettlement(
            address from,
            bytes32 currencyKey,
            uint amount,
            uint refunded
        ) external view returns (uint amountAfterSettlement);
    
        function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool);
    
        function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint);
    
        function settlementOwing(address account, bytes32 currencyKey)
            external
            view
            returns (
                uint reclaimAmount,
                uint rebateAmount,
                uint numEntries
            );
    
        function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool);
    
        function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
            external
            view
            returns (uint exchangeFeeRate);
    
        function getAmountsForExchange(
            uint sourceAmount,
            bytes32 sourceCurrencyKey,
            bytes32 destinationCurrencyKey
        )
            external
            view
            returns (
                uint amountReceived,
                uint fee,
                uint exchangeFeeRate
            );
    
        function priceDeviationThresholdFactor() external view returns (uint);
    
        function waitingPeriodSecs() external view returns (uint);
    
        // Mutative functions
        function exchange(
            address exchangeForAddress,
            address from,
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address destinationAddress,
            bool virtualSynth,
            address rewardAddress,
            bytes32 trackingCode
        ) external returns (uint amountReceived, IVirtualSynth vSynth);
    
        function settle(address from, bytes32 currencyKey)
            external
            returns (
                uint reclaimed,
                uint refunded,
                uint numEntries
            );
    
        function setLastExchangeRateForSynth(bytes32 currencyKey, uint rate) external;
    
        function resetLastExchangeRate(bytes32[] calldata currencyKeys) external;
    
        function suspendSynthWithInvalidRate(bytes32 currencyKey) external;
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/irewardsdistribution
    interface IRewardsDistribution {
        // Structs
        struct DistributionData {
            address destination;
            uint amount;
        }
    
        // Views
        function authority() external view returns (address);
    
        function distributions(uint index) external view returns (address destination, uint amount); // DistributionData
    
        function distributionsLength() external view returns (uint);
    
        // Mutative Functions
        function distributeRewards(uint amount) external returns (bool);
    }
    
    
    // Inheritance
    
    
    // Internal references
    
    
    contract BaseSynthetix is IERC20, ExternStateToken, MixinResolver, ISynthetix {
        // ========== STATE VARIABLES ==========
    
        // Available Synths which can be used with the system
        string public constant TOKEN_NAME = "Synthetix Network Token";
        string public constant TOKEN_SYMBOL = "SNX";
        uint8 public constant DECIMALS = 18;
        bytes32 public constant sUSD = "sUSD";
    
        // ========== ADDRESS RESOLVER CONFIGURATION ==========
        bytes32 private constant CONTRACT_SYNTHETIXSTATE = "SynthetixState";
        bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
        bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
        bytes32 private constant CONTRACT_ISSUER = "Issuer";
        bytes32 private constant CONTRACT_REWARDSDISTRIBUTION = "RewardsDistribution";
    
        // ========== CONSTRUCTOR ==========
    
        constructor(
            address payable _proxy,
            TokenState _tokenState,
            address _owner,
            uint _totalSupply,
            address _resolver
        )
            public
            ExternStateToken(_proxy, _tokenState, TOKEN_NAME, TOKEN_SYMBOL, _totalSupply, DECIMALS, _owner)
            MixinResolver(_resolver)
        {}
    
        // ========== VIEWS ==========
    
        // Note: use public visibility so that it can be invoked in a subclass
        function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
            addresses = new bytes32[](5);
            addresses[0] = CONTRACT_SYNTHETIXSTATE;
            addresses[1] = CONTRACT_SYSTEMSTATUS;
            addresses[2] = CONTRACT_EXCHANGER;
            addresses[3] = CONTRACT_ISSUER;
            addresses[4] = CONTRACT_REWARDSDISTRIBUTION;
        }
    
        function synthetixState() internal view returns (ISynthetixState) {
            return ISynthetixState(requireAndGetAddress(CONTRACT_SYNTHETIXSTATE));
        }
    
        function systemStatus() internal view returns (ISystemStatus) {
            return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
        }
    
        function exchanger() internal view returns (IExchanger) {
            return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER));
        }
    
        function issuer() internal view returns (IIssuer) {
            return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
        }
    
        function rewardsDistribution() internal view returns (IRewardsDistribution) {
            return IRewardsDistribution(requireAndGetAddress(CONTRACT_REWARDSDISTRIBUTION));
        }
    
        function debtBalanceOf(address account, bytes32 currencyKey) external view returns (uint) {
            return issuer().debtBalanceOf(account, currencyKey);
        }
    
        function totalIssuedSynths(bytes32 currencyKey) external view returns (uint) {
            return issuer().totalIssuedSynths(currencyKey, false);
        }
    
        function totalIssuedSynthsExcludeOtherCollateral(bytes32 currencyKey) external view returns (uint) {
            return issuer().totalIssuedSynths(currencyKey, true);
        }
    
        function availableCurrencyKeys() external view returns (bytes32[] memory) {
            return issuer().availableCurrencyKeys();
        }
    
        function availableSynthCount() external view returns (uint) {
            return issuer().availableSynthCount();
        }
    
        function availableSynths(uint index) external view returns (ISynth) {
            return issuer().availableSynths(index);
        }
    
        function synths(bytes32 currencyKey) external view returns (ISynth) {
            return issuer().synths(currencyKey);
        }
    
        function synthsByAddress(address synthAddress) external view returns (bytes32) {
            return issuer().synthsByAddress(synthAddress);
        }
    
        function isWaitingPeriod(bytes32 currencyKey) external view returns (bool) {
            return exchanger().maxSecsLeftInWaitingPeriod(messageSender, currencyKey) > 0;
        }
    
        function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid) {
            return issuer().anySynthOrSNXRateIsInvalid();
        }
    
        function maxIssuableSynths(address account) external view returns (uint maxIssuable) {
            return issuer().maxIssuableSynths(account);
        }
    
        function remainingIssuableSynths(address account)
            external
            view
            returns (
                uint maxIssuable,
                uint alreadyIssued,
                uint totalSystemDebt
            )
        {
            return issuer().remainingIssuableSynths(account);
        }
    
        function collateralisationRatio(address _issuer) external view returns (uint) {
            return issuer().collateralisationRatio(_issuer);
        }
    
        function collateral(address account) external view returns (uint) {
            return issuer().collateral(account);
        }
    
        function transferableSynthetix(address account) external view returns (uint transferable) {
            (transferable, ) = issuer().transferableSynthetixAndAnyRateIsInvalid(account, tokenState.balanceOf(account));
        }
    
        function _canTransfer(address account, uint value) internal view returns (bool) {
            (uint initialDebtOwnership, ) = synthetixState().issuanceData(account);
    
            if (initialDebtOwnership > 0) {
                (uint transferable, bool anyRateIsInvalid) =
                    issuer().transferableSynthetixAndAnyRateIsInvalid(account, tokenState.balanceOf(account));
                require(value <= transferable, "Cannot transfer staked or escrowed SNX");
                require(!anyRateIsInvalid, "A synth or SNX rate is invalid");
            }
            return true;
        }
    
        // ========== MUTATIVE FUNCTIONS ==========
    
        function exchange(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey
        ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) {
            (amountReceived, ) = exchanger().exchange(
                messageSender,
                messageSender,
                sourceCurrencyKey,
                sourceAmount,
                destinationCurrencyKey,
                messageSender,
                false,
                messageSender,
                bytes32(0)
            );
        }
    
        function exchangeOnBehalf(
            address exchangeForAddress,
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey
        ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) {
            (amountReceived, ) = exchanger().exchange(
                exchangeForAddress,
                messageSender,
                sourceCurrencyKey,
                sourceAmount,
                destinationCurrencyKey,
                exchangeForAddress,
                false,
                exchangeForAddress,
                bytes32(0)
            );
        }
    
        function settle(bytes32 currencyKey)
            external
            optionalProxy
            returns (
                uint reclaimed,
                uint refunded,
                uint numEntriesSettled
            )
        {
            return exchanger().settle(messageSender, currencyKey);
        }
    
        function exchangeWithTracking(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) {
            (amountReceived, ) = exchanger().exchange(
                messageSender,
                messageSender,
                sourceCurrencyKey,
                sourceAmount,
                destinationCurrencyKey,
                messageSender,
                false,
                rewardAddress,
                trackingCode
            );
        }
    
        function exchangeOnBehalfWithTracking(
            address exchangeForAddress,
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) {
            (amountReceived, ) = exchanger().exchange(
                exchangeForAddress,
                messageSender,
                sourceCurrencyKey,
                sourceAmount,
                destinationCurrencyKey,
                exchangeForAddress,
                false,
                rewardAddress,
                trackingCode
            );
        }
    
        function transfer(address to, uint value) external optionalProxy systemActive returns (bool) {
            // Ensure they're not trying to exceed their locked amount -- only if they have debt.
            _canTransfer(messageSender, value);
    
            // Perform the transfer: if there is a problem an exception will be thrown in this call.
            _transferByProxy(messageSender, to, value);
    
            return true;
        }
    
        function transferFrom(
            address from,
            address to,
            uint value
        ) external optionalProxy systemActive returns (bool) {
            // Ensure they're not trying to exceed their locked amount -- only if they have debt.
            _canTransfer(from, value);
    
            // Perform the transfer: if there is a problem,
            // an exception will be thrown in this call.
            return _transferFromByProxy(messageSender, from, to, value);
        }
    
        function issueSynths(uint amount) external issuanceActive optionalProxy {
            return issuer().issueSynths(messageSender, amount);
        }
    
        function issueSynthsOnBehalf(address issueForAddress, uint amount) external issuanceActive optionalProxy {
            return issuer().issueSynthsOnBehalf(issueForAddress, messageSender, amount);
        }
    
        function issueMaxSynths() external issuanceActive optionalProxy {
            return issuer().issueMaxSynths(messageSender);
        }
    
        function issueMaxSynthsOnBehalf(address issueForAddress) external issuanceActive optionalProxy {
            return issuer().issueMaxSynthsOnBehalf(issueForAddress, messageSender);
        }
    
        function burnSynths(uint amount) external issuanceActive optionalProxy {
            return issuer().burnSynths(messageSender, amount);
        }
    
        function burnSynthsOnBehalf(address burnForAddress, uint amount) external issuanceActive optionalProxy {
            return issuer().burnSynthsOnBehalf(burnForAddress, messageSender, amount);
        }
    
        function burnSynthsToTarget() external issuanceActive optionalProxy {
            return issuer().burnSynthsToTarget(messageSender);
        }
    
        function burnSynthsToTargetOnBehalf(address burnForAddress) external issuanceActive optionalProxy {
            return issuer().burnSynthsToTargetOnBehalf(burnForAddress, messageSender);
        }
    
        function exchangeWithTrackingForInitiator(
            bytes32,
            uint,
            bytes32,
            address,
            bytes32
        ) external returns (uint amountReceived) {
            _notImplemented();
        }
    
        function exchangeWithVirtual(
            bytes32,
            uint,
            bytes32,
            bytes32
        ) external returns (uint, IVirtualSynth) {
            _notImplemented();
        }
    
        function mint() external returns (bool) {
            _notImplemented();
        }
    
        function liquidateDelinquentAccount(address, uint) external returns (bool) {
            _notImplemented();
        }
    
        function mintSecondary(address, uint) external {
            _notImplemented();
        }
    
        function mintSecondaryRewards(uint) external {
            _notImplemented();
        }
    
        function burnSecondary(address, uint) external {
            _notImplemented();
        }
    
        function _notImplemented() internal pure {
            revert("Cannot be run on this layer");
        }
    
        // ========== MODIFIERS ==========
    
        modifier systemActive() {
            _systemActive();
            _;
        }
    
        function _systemActive() private {
            systemStatus().requireSystemActive();
        }
    
        modifier issuanceActive() {
            _issuanceActive();
            _;
        }
    
        function _issuanceActive() private {
            systemStatus().requireIssuanceActive();
        }
    
        modifier exchangeActive(bytes32 src, bytes32 dest) {
            _exchangeActive(src, dest);
            _;
        }
    
        function _exchangeActive(bytes32 src, bytes32 dest) private {
            systemStatus().requireExchangeBetweenSynthsAllowed(src, dest);
        }
    
        modifier onlyExchanger() {
            _onlyExchanger();
            _;
        }
    
        function _onlyExchanger() private {
            require(msg.sender == address(exchanger()), "Only Exchanger can invoke this");
        }
    
        // ========== EVENTS ==========
        event SynthExchange(
            address indexed account,
            bytes32 fromCurrencyKey,
            uint256 fromAmount,
            bytes32 toCurrencyKey,
            uint256 toAmount,
            address toAddress
        );
        bytes32 internal constant SYNTHEXCHANGE_SIG =
            keccak256("SynthExchange(address,bytes32,uint256,bytes32,uint256,address)");
    
        function emitSynthExchange(
            address account,
            bytes32 fromCurrencyKey,
            uint256 fromAmount,
            bytes32 toCurrencyKey,
            uint256 toAmount,
            address toAddress
        ) external onlyExchanger {
            proxy._emit(
                abi.encode(fromCurrencyKey, fromAmount, toCurrencyKey, toAmount, toAddress),
                2,
                SYNTHEXCHANGE_SIG,
                addressToBytes32(account),
                0,
                0
            );
        }
    
        event ExchangeTracking(bytes32 indexed trackingCode, bytes32 toCurrencyKey, uint256 toAmount, uint256 fee);
        bytes32 internal constant EXCHANGE_TRACKING_SIG = keccak256("ExchangeTracking(bytes32,bytes32,uint256,uint256)");
    
        function emitExchangeTracking(
            bytes32 trackingCode,
            bytes32 toCurrencyKey,
            uint256 toAmount,
            uint256 fee
        ) external onlyExchanger {
            proxy._emit(abi.encode(toCurrencyKey, toAmount, fee), 2, EXCHANGE_TRACKING_SIG, trackingCode, 0, 0);
        }
    
        event ExchangeReclaim(address indexed account, bytes32 currencyKey, uint amount);
        bytes32 internal constant EXCHANGERECLAIM_SIG = keccak256("ExchangeReclaim(address,bytes32,uint256)");
    
        function emitExchangeReclaim(
            address account,
            bytes32 currencyKey,
            uint256 amount
        ) external onlyExchanger {
            proxy._emit(abi.encode(currencyKey, amount), 2, EXCHANGERECLAIM_SIG, addressToBytes32(account), 0, 0);
        }
    
        event ExchangeRebate(address indexed account, bytes32 currencyKey, uint amount);
        bytes32 internal constant EXCHANGEREBATE_SIG = keccak256("ExchangeRebate(address,bytes32,uint256)");
    
        function emitExchangeRebate(
            address account,
            bytes32 currencyKey,
            uint256 amount
        ) external onlyExchanger {
            proxy._emit(abi.encode(currencyKey, amount), 2, EXCHANGEREBATE_SIG, addressToBytes32(account), 0, 0);
        }
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/irewardescrow
    interface IRewardEscrow {
        // Views
        function balanceOf(address account) external view returns (uint);
    
        function numVestingEntries(address account) external view returns (uint);
    
        function totalEscrowedAccountBalance(address account) external view returns (uint);
    
        function totalVestedAccountBalance(address account) external view returns (uint);
    
        function getVestingScheduleEntry(address account, uint index) external view returns (uint[2] memory);
    
        function getNextVestingIndex(address account) external view returns (uint);
    
        // Mutative functions
        function appendVestingEntry(address account, uint quantity) external;
    
        function vest() external;
    }
    
    
    pragma experimental ABIEncoderV2;
    
    library VestingEntries {
        struct VestingEntry {
            uint64 endTime;
            uint256 escrowAmount;
        }
        struct VestingEntryWithID {
            uint64 endTime;
            uint256 escrowAmount;
            uint256 entryID;
        }
    }
    
    interface IRewardEscrowV2 {
        // Views
        function balanceOf(address account) external view returns (uint);
    
        function numVestingEntries(address account) external view returns (uint);
    
        function totalEscrowedAccountBalance(address account) external view returns (uint);
    
        function totalVestedAccountBalance(address account) external view returns (uint);
    
        function getVestingQuantity(address account, uint256[] calldata entryIDs) external view returns (uint);
    
        function getVestingSchedules(
            address account,
            uint256 index,
            uint256 pageSize
        ) external view returns (VestingEntries.VestingEntryWithID[] memory);
    
        function getAccountVestingEntryIDs(
            address account,
            uint256 index,
            uint256 pageSize
        ) external view returns (uint256[] memory);
    
        function getVestingEntryClaimable(address account, uint256 entryID) external view returns (uint);
    
        function getVestingEntry(address account, uint256 entryID) external view returns (uint64, uint256);
    
        // Mutative functions
        function vest(uint256[] calldata entryIDs) external;
    
        function createEscrowEntry(
            address beneficiary,
            uint256 deposit,
            uint256 duration
        ) external;
    
        function appendVestingEntry(
            address account,
            uint256 quantity,
            uint256 duration
        ) external;
    
        function migrateVestingSchedule(address _addressToMigrate) external;
    
        function migrateAccountEscrowBalances(
            address[] calldata accounts,
            uint256[] calldata escrowBalances,
            uint256[] calldata vestedBalances
        ) external;
    
        // Account Merging
        function startMergingWindow() external;
    
        function mergeAccount(address accountToMerge, uint256[] calldata entryIDs) external;
    
        function nominateAccountToMerge(address account) external;
    
        function accountMergingIsOpen() external view returns (bool);
    
        // L2 Migration
        function importVestingEntries(
            address account,
            uint256 escrowedAmount,
            VestingEntries.VestingEntry[] calldata vestingEntries
        ) external;
    
        // Return amount of SNX transfered to SynthetixBridgeToOptimism deposit contract
        function burnForMigration(address account, uint256[] calldata entryIDs)
            external
            returns (uint256 escrowedAccountBalance, VestingEntries.VestingEntry[] memory vestingEntries);
    }
    
    
    // https://docs.synthetix.io/contracts/source/interfaces/isupplyschedule
    interface ISupplySchedule {
        // Views
        function mintableSupply() external view returns (uint);
    
        function isMintable() external view returns (bool);
    
        function minterReward() external view returns (uint);
    
        // Mutative functions
        function recordMintEvent(uint supplyMinted) external returns (bool);
    }
    
    
    // Inheritance
    
    
    // Internal references
    
    
    // https://docs.synthetix.io/contracts/source/contracts/synthetix
    contract Synthetix is BaseSynthetix {
        bytes32 public constant CONTRACT_NAME = "Synthetix";
    
        // ========== ADDRESS RESOLVER CONFIGURATION ==========
        bytes32 private constant CONTRACT_REWARD_ESCROW = "RewardEscrow";
        bytes32 private constant CONTRACT_REWARDESCROW_V2 = "RewardEscrowV2";
        bytes32 private constant CONTRACT_SUPPLYSCHEDULE = "SupplySchedule";
    
        // ========== CONSTRUCTOR ==========
    
        constructor(
            address payable _proxy,
            TokenState _tokenState,
            address _owner,
            uint _totalSupply,
            address _resolver
        ) public BaseSynthetix(_proxy, _tokenState, _owner, _totalSupply, _resolver) {}
    
        function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
            bytes32[] memory existingAddresses = BaseSynthetix.resolverAddressesRequired();
            bytes32[] memory newAddresses = new bytes32[](3);
            newAddresses[0] = CONTRACT_REWARD_ESCROW;
            newAddresses[1] = CONTRACT_REWARDESCROW_V2;
            newAddresses[2] = CONTRACT_SUPPLYSCHEDULE;
            return combineArrays(existingAddresses, newAddresses);
        }
    
        // ========== VIEWS ==========
    
        function rewardEscrow() internal view returns (IRewardEscrow) {
            return IRewardEscrow(requireAndGetAddress(CONTRACT_REWARD_ESCROW));
        }
    
        function rewardEscrowV2() internal view returns (IRewardEscrowV2) {
            return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW_V2));
        }
    
        function supplySchedule() internal view returns (ISupplySchedule) {
            return ISupplySchedule(requireAndGetAddress(CONTRACT_SUPPLYSCHEDULE));
        }
    
        // ========== OVERRIDDEN FUNCTIONS ==========
    
        function exchangeWithVirtual(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            bytes32 trackingCode
        )
            external
            exchangeActive(sourceCurrencyKey, destinationCurrencyKey)
            optionalProxy
            returns (uint amountReceived, IVirtualSynth vSynth)
        {
            return
                exchanger().exchange(
                    messageSender,
                    messageSender,
                    sourceCurrencyKey,
                    sourceAmount,
                    destinationCurrencyKey,
                    messageSender,
                    true,
                    messageSender,
                    trackingCode
                );
        }
    
        // SIP-140 The initiating user of this exchange will receive the proceeds of the exchange
        // Note: this function may have unintended consequences if not understood correctly. Please
        // read SIP-140 for more information on the use-case
        function exchangeWithTrackingForInitiator(
            bytes32 sourceCurrencyKey,
            uint sourceAmount,
            bytes32 destinationCurrencyKey,
            address rewardAddress,
            bytes32 trackingCode
        ) external exchangeActive(sourceCurrencyKey, destinationCurrencyKey) optionalProxy returns (uint amountReceived) {
            (amountReceived, ) = exchanger().exchange(
                messageSender,
                messageSender,
                sourceCurrencyKey,
                sourceAmount,
                destinationCurrencyKey,
                // solhint-disable avoid-tx-origin
                tx.origin,
                false,
                rewardAddress,
                trackingCode
            );
        }
    
        function settle(bytes32 currencyKey)
            external
            optionalProxy
            returns (
                uint reclaimed,
                uint refunded,
                uint numEntriesSettled
            )
        {
            return exchanger().settle(messageSender, currencyKey);
        }
    
        function mint() external issuanceActive returns (bool) {
            require(address(rewardsDistribution()) != address(0), "RewardsDistribution not set");
    
            ISupplySchedule _supplySchedule = supplySchedule();
            IRewardsDistribution _rewardsDistribution = rewardsDistribution();
    
            uint supplyToMint = _supplySchedule.mintableSupply();
            require(supplyToMint > 0, "No supply is mintable");
    
            // record minting event before mutation to token supply
            _supplySchedule.recordMintEvent(supplyToMint);
    
            // Set minted SNX balance to RewardEscrow's balance
            // Minus the minterReward and set balance of minter to add reward
            uint minterReward = _supplySchedule.minterReward();
            // Get the remainder
            uint amountToDistribute = supplyToMint.sub(minterReward);
    
            // Set the token balance to the RewardsDistribution contract
            tokenState.setBalanceOf(
                address(_rewardsDistribution),
                tokenState.balanceOf(address(_rewardsDistribution)).add(amountToDistribute)
            );
            emitTransfer(address(this), address(_rewardsDistribution), amountToDistribute);
    
            // Kick off the distribution of rewards
            _rewardsDistribution.distributeRewards(amountToDistribute);
    
            // Assign the minters reward.
            tokenState.setBalanceOf(msg.sender, tokenState.balanceOf(msg.sender).add(minterReward));
            emitTransfer(address(this), msg.sender, minterReward);
    
            totalSupply = totalSupply.add(supplyToMint);
    
            return true;
        }
    
        function liquidateDelinquentAccount(address account, uint susdAmount)
            external
            systemActive
            optionalProxy
            returns (bool)
        {
            (uint totalRedeemed, uint amountLiquidated) =
                issuer().liquidateDelinquentAccount(account, susdAmount, messageSender);
    
            emitAccountLiquidated(account, totalRedeemed, amountLiquidated, messageSender);
    
            // Transfer SNX redeemed to messageSender
            // Reverts if amount to redeem is more than balanceOf account, ie due to escrowed balance
            return _transferByProxy(account, messageSender, totalRedeemed);
        }
    
        /* Once off function for SIP-60 to migrate SNX balances in the RewardEscrow contract
         * To the new RewardEscrowV2 contract
         */
        function migrateEscrowBalanceToRewardEscrowV2() external onlyOwner {
            // Record balanceOf(RewardEscrow) contract
            uint rewardEscrowBalance = tokenState.balanceOf(address(rewardEscrow()));
    
            // transfer all of RewardEscrow's balance to RewardEscrowV2
            // _internalTransfer emits the transfer event
            _internalTransfer(address(rewardEscrow()), address(rewardEscrowV2()), rewardEscrowBalance);
        }
    
        // ========== EVENTS ==========
        event AccountLiquidated(address indexed account, uint snxRedeemed, uint amountLiquidated, address liquidator);
        bytes32 internal constant ACCOUNTLIQUIDATED_SIG = keccak256("AccountLiquidated(address,uint256,uint256,address)");
    
        function emitAccountLiquidated(
            address account,
            uint256 snxRedeemed,
            uint256 amountLiquidated,
            address liquidator
        ) internal {
            proxy._emit(
                abi.encode(snxRedeemed, amountLiquidated, liquidator),
                2,
                ACCOUNTLIQUIDATED_SIG,
                addressToBytes32(account),
                0,
                0
            );
        }
    }