ETH Price: $1,880.62 (+0.59%)
Gas: 0.82 Gwei

Transaction Decoder

Block:
11378032 at Dec-03-2020 06:32:52 AM +UTC
Transaction Fee:
0.00095808 ETH $1.80
Gas Used:
31,936 Gas / 30 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x05593240...10FcfE25A
0x76043f4D...d7eABf586
10.347553612455071975 Eth
Nonce: 269546
10.346595532455071975 Eth
Nonce: 269547
0.00095808
(Ethermine)
571.679541757839669986 Eth571.680499837839669986 Eth0.00095808

Execution Trace

ConversionRates.setCompactData( buy=[7+/w7wTh7+/z+frvAAA=], sell=[EREQEf8fERENDwULAAA=], blockNumber=11378031, indices=[0] )
{"ConversionRates.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../../ERC20Interface.sol\";\nimport \"../../Utils.sol\";\nimport \"../../ConversionRatesInterface.sol\";\nimport \"../VolumeImbalanceRecorder.sol\";\n\n\ncontract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils {\n\n    // bps - basic rate steps. one step is 1 / 10000 of the rate.\n    struct StepFunction {\n        int[] x; // quantity for each step. Quantity of each step includes previous steps.\n        int[] y; // rate change per quantity step  in bps.\n    }\n\n    struct TokenData {\n        bool listed;  // was added to reserve\n        bool enabled; // whether trade is enabled\n\n        // position in the compact data\n        uint compactDataArrayIndex;\n        uint compactDataFieldIndex;\n\n        // rate data. base and changes according to quantity and reserve balance.\n        // generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction.\n        uint baseBuyRate;  // in PRECISION units. see KyberConstants\n        uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate\n        StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate.\n        StepFunction sellRateQtyStepFunction;// in bps. higher the qua\n        StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate.\n        StepFunction sellRateImbalanceStepFunction;\n    }\n\n    /*\n    this is the data for tokenRatesCompactData\n    but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write\n    so we represent it as bytes32 and do the byte tricks ourselves.\n    struct TokenRatesCompactData {\n        bytes14 buy;  // change buy rate of token from baseBuyRate in 10 bps\n        bytes14 sell; // change sell rate of token from baseSellRate in 10 bps\n\n        uint32 blockNumber;\n    } */\n    uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks\n    ERC20[] internal listedTokens;\n    mapping(address=\u003eTokenData) internal tokenData;\n    bytes32[] internal tokenRatesCompactData;\n    uint public numTokensInCurrentCompactData = 0;\n    address public reserveContract;\n    uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14;\n    uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA));\n    uint constant internal MAX_STEPS_IN_FUNCTION = 10;\n    int  constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B %\n    int  constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100%\n\n    function ConversionRates(address _admin) public VolumeImbalanceRecorder(_admin)\n        { } // solhint-disable-line no-empty-blocks\n\n    function addToken(ERC20 token) public onlyAdmin {\n\n        require(!tokenData[token].listed);\n        tokenData[token].listed = true;\n        listedTokens.push(token);\n\n        if (numTokensInCurrentCompactData == 0) {\n            tokenRatesCompactData.length++; // add new structure\n        }\n\n        tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1;\n        tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData;\n\n        numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA;\n\n        setGarbageToVolumeRecorder(token);\n\n        setDecimals(token);\n    }\n\n    function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator {\n\n        require(buy.length == sell.length);\n        require(indices.length == buy.length);\n        require(blockNumber \u003c= 0xFFFFFFFF);\n\n        uint bytes14Offset = BYTES_14_OFFSET;\n\n        for (uint i = 0; i \u003c indices.length; i++) {\n            require(indices[i] \u003c tokenRatesCompactData.length);\n            uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset));\n            tokenRatesCompactData[indices[i]] = bytes32(data);\n        }\n    }\n\n    function setBaseRate(\n        ERC20[] tokens,\n        uint[] baseBuy,\n        uint[] baseSell,\n        bytes14[] buy,\n        bytes14[] sell,\n        uint blockNumber,\n        uint[] indices\n    )\n        public\n        onlyOperator\n    {\n        require(tokens.length == baseBuy.length);\n        require(tokens.length == baseSell.length);\n        require(sell.length == buy.length);\n        require(sell.length == indices.length);\n\n        for (uint ind = 0; ind \u003c tokens.length; ind++) {\n            require(tokenData[tokens[ind]].listed);\n            tokenData[tokens[ind]].baseBuyRate = baseBuy[ind];\n            tokenData[tokens[ind]].baseSellRate = baseSell[ind];\n        }\n\n        setCompactData(buy, sell, blockNumber, indices);\n    }\n\n    function setQtyStepFunction(\n        ERC20 token,\n        int[] xBuy,\n        int[] yBuy,\n        int[] xSell,\n        int[] ySell\n    )\n        public\n        onlyOperator\n    {\n        require(xBuy.length == yBuy.length);\n        require(xSell.length == ySell.length);\n        require(xBuy.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(xSell.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(tokenData[token].listed);\n\n        tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy);\n        tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell);\n    }\n\n    function setImbalanceStepFunction(\n        ERC20 token,\n        int[] xBuy,\n        int[] yBuy,\n        int[] xSell,\n        int[] ySell\n    )\n        public\n        onlyOperator\n    {\n        require(xBuy.length == yBuy.length);\n        require(xSell.length == ySell.length);\n        require(xBuy.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(xSell.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(tokenData[token].listed);\n\n        tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy);\n        tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell);\n    }\n\n    function setValidRateDurationInBlocks(uint duration) public onlyAdmin {\n        validRateDurationInBlocks = duration;\n    }\n\n    function enableTokenTrade(ERC20 token) public onlyAdmin {\n        require(tokenData[token].listed);\n        require(tokenControlInfo[token].minimalRecordResolution != 0);\n        tokenData[token].enabled = true;\n    }\n\n    function disableTokenTrade(ERC20 token) public onlyAlerter {\n        require(tokenData[token].listed);\n        tokenData[token].enabled = false;\n    }\n\n    function setReserveAddress(address reserve) public onlyAdmin {\n        reserveContract = reserve;\n    }\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public\n    {\n        require(msg.sender == reserveContract);\n\n        if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token);\n\n        return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock);\n    }\n\n    /* solhint-disable function-max-lines */\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) {\n        // check if trade is enabled\n        if (!tokenData[token].enabled) return 0;\n        if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set\n\n        // get rate update block\n        bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];\n\n        uint updateRateBlock = getLast4Bytes(compactData);\n        if (currentBlockNumber \u003e= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired\n        // check imbalance\n        int totalImbalance;\n        int blockImbalance;\n        (totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber);\n\n        // calculate actual rate\n        int imbalanceQty;\n        int extraBps;\n        int8 rateUpdate;\n        uint rate;\n\n        if (buy) {\n            // start with base rate\n            rate = tokenData[token].baseBuyRate;\n\n            // add rate update\n            rateUpdate = getRateByteFromCompactData(compactData, token, true);\n            extraBps = int(rateUpdate) * 10;\n            rate = addBps(rate, extraBps);\n\n            // compute token qty\n            qty = getTokenQty(token, rate, qty);\n            imbalanceQty = int(qty);\n            totalImbalance += imbalanceQty;\n\n            // add qty overhead\n            extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty));\n            rate = addBps(rate, extraBps);\n\n            // add imbalance overhead\n            extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance);\n            rate = addBps(rate, extraBps);\n        } else {\n            // start with base rate\n            rate = tokenData[token].baseSellRate;\n\n            // add rate update\n            rateUpdate = getRateByteFromCompactData(compactData, token, false);\n            extraBps = int(rateUpdate) * 10;\n            rate = addBps(rate, extraBps);\n\n            // compute token qty\n            imbalanceQty = -1 * int(qty);\n            totalImbalance += imbalanceQty;\n\n            // add qty overhead\n            extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty));\n            rate = addBps(rate, extraBps);\n\n            // add imbalance overhead\n            extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance);\n            rate = addBps(rate, extraBps);\n        }\n\n        if (abs(totalImbalance) \u003e= getMaxTotalImbalance(token)) return 0;\n        if (abs(blockImbalance + imbalanceQty) \u003e= getMaxPerBlockImbalance(token)) return 0;\n\n        return rate;\n    }\n    /* solhint-enable function-max-lines */\n\n    function getBasicRate(ERC20 token, bool buy) public view returns(uint) {\n        if (buy)\n            return tokenData[token].baseBuyRate;\n        else\n            return tokenData[token].baseSellRate;\n    }\n\n    function getCompactData(ERC20 token) public view returns(uint, uint, byte, byte) {\n        require(tokenData[token].listed);\n\n        uint arrayIndex = tokenData[token].compactDataArrayIndex;\n        uint fieldOffset = tokenData[token].compactDataFieldIndex;\n\n        return (\n            arrayIndex,\n            fieldOffset,\n            byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)),\n            byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false))\n        );\n    }\n\n    function getTokenBasicData(ERC20 token) public view returns(bool, bool) {\n        return (tokenData[token].listed, tokenData[token].enabled);\n    }\n\n    /* solhint-disable code-complexity */\n    function getStepFunctionData(ERC20 token, uint command, uint param) public view returns(int) {\n        if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length);\n        if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param];\n        if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length);\n        if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param];\n\n        if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length);\n        if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param];\n        if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length);\n        if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param];\n\n        if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length);\n        if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param];\n        if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length);\n        if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param];\n\n        if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length);\n        if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param];\n        if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length);\n        if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param];\n\n        revert();\n    }\n    /* solhint-enable code-complexity */\n\n    function getRateUpdateBlock(ERC20 token) public view returns(uint) {\n        bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];\n        return getLast4Bytes(compactData);\n    }\n\n    function getListedTokens() public view returns(ERC20[]) {\n        return listedTokens;\n    }\n\n    function getTokenQty(ERC20 token, uint ethQty, uint rate) internal view returns(uint) {\n        uint dstDecimals = getDecimals(token);\n        uint srcDecimals = ETH_DECIMALS;\n\n        return calcDstQty(ethQty, srcDecimals, dstDecimals, rate);\n    }\n\n    function getLast4Bytes(bytes32 b) internal pure returns(uint) {\n        // cannot trust compiler with not turning bit operations into EXP opcode\n        return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET);\n    }\n\n    function getRateByteFromCompactData(bytes32 data, ERC20 token, bool buy) internal view returns(int8) {\n        uint fieldOffset = tokenData[token].compactDataFieldIndex;\n        uint byteOffset;\n        if (buy)\n            byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset;\n        else\n            byteOffset = 4 + fieldOffset;\n\n        return int8(data[byteOffset]);\n    }\n\n    function executeStepFunction(StepFunction f, int x) internal pure returns(int) {\n        uint len = f.y.length;\n        for (uint ind = 0; ind \u003c len; ind++) {\n            if (x \u003c= f.x[ind]) return f.y[ind];\n        }\n\n        return f.y[len-1];\n    }\n\n    function addBps(uint rate, int bps) internal pure returns(uint) {\n        require(rate \u003c= MAX_RATE);\n        require(bps \u003e= MIN_BPS_ADJUSTMENT);\n        require(bps \u003c= MAX_BPS_ADJUSTMENT);\n\n        uint maxBps = 100 * 100;\n        return (rate * uint(int(maxBps) + bps)) / maxBps;\n    }\n\n    function abs(int x) internal pure returns(uint) {\n        if (x \u003c 0)\n            return uint(-1 * x);\n        else\n            return uint(x);\n    }\n}\n"},"ConversionRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ConversionRatesInterface {\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public;\n\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);\n}\n"},"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n    address public admin;\n    address public pendingAdmin;\n    mapping(address=\u003ebool) internal operators;\n    mapping(address=\u003ebool) internal alerters;\n    address[] internal operatorsGroup;\n    address[] internal alertersGroup;\n    uint constant internal MAX_GROUP_SIZE = 50;\n\n    function PermissionGroups() public {\n        admin = msg.sender;\n    }\n\n    modifier onlyAdmin() {\n        require(msg.sender == admin);\n        _;\n    }\n\n    modifier onlyOperator() {\n        require(operators[msg.sender]);\n        _;\n    }\n\n    modifier onlyAlerter() {\n        require(alerters[msg.sender]);\n        _;\n    }\n\n    function getOperators () external view returns(address[]) {\n        return operatorsGroup;\n    }\n\n    function getAlerters () external view returns(address[]) {\n        return alertersGroup;\n    }\n\n    event TransferAdminPending(address pendingAdmin);\n\n    /**\n     * @dev Allows the current admin to set the pendingAdmin address.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdmin(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(pendingAdmin);\n        pendingAdmin = newAdmin;\n    }\n\n    /**\n     * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdminQuickly(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(newAdmin);\n        AdminClaimed(newAdmin, admin);\n        admin = newAdmin;\n    }\n\n    event AdminClaimed( address newAdmin, address previousAdmin);\n\n    /**\n     * @dev Allows the pendingAdmin address to finalize the change admin process.\n     */\n    function claimAdmin() public {\n        require(pendingAdmin == msg.sender);\n        AdminClaimed(pendingAdmin, admin);\n        admin = pendingAdmin;\n        pendingAdmin = address(0);\n    }\n\n    event AlerterAdded (address newAlerter, bool isAdd);\n\n    function addAlerter(address newAlerter) public onlyAdmin {\n        require(!alerters[newAlerter]); // prevent duplicates.\n        require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n        AlerterAdded(newAlerter, true);\n        alerters[newAlerter] = true;\n        alertersGroup.push(newAlerter);\n    }\n\n    function removeAlerter (address alerter) public onlyAdmin {\n        require(alerters[alerter]);\n        alerters[alerter] = false;\n\n        for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n            if (alertersGroup[i] == alerter) {\n                alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n                alertersGroup.length--;\n                AlerterAdded(alerter, false);\n                break;\n            }\n        }\n    }\n\n    event OperatorAdded(address newOperator, bool isAdd);\n\n    function addOperator(address newOperator) public onlyAdmin {\n        require(!operators[newOperator]); // prevent duplicates.\n        require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n        OperatorAdded(newOperator, true);\n        operators[newOperator] = true;\n        operatorsGroup.push(newOperator);\n    }\n\n    function removeOperator (address operator) public onlyAdmin {\n        require(operators[operator]);\n        operators[operator] = false;\n\n        for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n            if (operatorsGroup[i] == operator) {\n                operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n                operatorsGroup.length -= 1;\n                OperatorAdded(operator, false);\n                break;\n            }\n        }\n    }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"VolumeImbalanceRecorder.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../ERC20Interface.sol\";\nimport \"../Withdrawable.sol\";\n\n\ncontract VolumeImbalanceRecorder is Withdrawable {\n\n    uint constant internal SLIDING_WINDOW_SIZE = 5;\n    uint constant internal POW_2_64 = 2 ** 64;\n\n    struct TokenControlInfo {\n        uint minimalRecordResolution; // can be roughly 1 cent\n        uint maxPerBlockImbalance; // in twei resolution\n        uint maxTotalImbalance; // max total imbalance (between rate updates)\n                            // before halting trade\n    }\n\n    mapping(address =\u003e TokenControlInfo) internal tokenControlInfo;\n\n    struct TokenImbalanceData {\n        int  lastBlockBuyUnitsImbalance;\n        uint lastBlock;\n\n        int  totalBuyUnitsImbalance;\n        uint lastRateUpdateBlock;\n    }\n\n    mapping(address =\u003e mapping(uint=\u003euint)) public tokenImbalanceData;\n\n    function VolumeImbalanceRecorder(address _admin) public {\n        require(_admin != address(0));\n        admin = _admin;\n    }\n\n    function setTokenControlInfo(\n        ERC20 token,\n        uint minimalRecordResolution,\n        uint maxPerBlockImbalance,\n        uint maxTotalImbalance\n    )\n        public\n        onlyAdmin\n    {\n        tokenControlInfo[token] =\n            TokenControlInfo(\n                minimalRecordResolution,\n                maxPerBlockImbalance,\n                maxTotalImbalance\n            );\n    }\n\n    function getTokenControlInfo(ERC20 token) public view returns(uint, uint, uint) {\n        return (tokenControlInfo[token].minimalRecordResolution,\n                tokenControlInfo[token].maxPerBlockImbalance,\n                tokenControlInfo[token].maxTotalImbalance);\n    }\n\n    function addImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        internal\n    {\n        uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE;\n        int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution));\n\n        int prevImbalance = 0;\n\n        TokenImbalanceData memory currentBlockData =\n            decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]);\n\n        // first scenario - this is not the first tx in the current block\n        if (currentBlockData.lastBlock == currentBlock) {\n            if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) {\n                // just increase imbalance\n                currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;\n                currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount;\n            } else {\n                // imbalance was changed in the middle of the block\n                prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock);\n                currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;\n                currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;\n                currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);\n            }\n        } else {\n            // first tx in the current block\n            int currentBlockImbalance;\n            (prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock);\n\n            currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount;\n            currentBlockData.lastBlock = uint(currentBlock);\n            currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);\n            currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;\n        }\n\n        tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData);\n    }\n\n    function setGarbageToVolumeRecorder(ERC20 token) internal {\n        for (uint i = 0; i \u003c SLIDING_WINDOW_SIZE; i++) {\n            tokenImbalanceData[token][i] = 0x1;\n        }\n    }\n\n    function getImbalanceInRange(ERC20 token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) {\n        // check the imbalance in the sliding window\n        require(startBlock \u003c= endBlock);\n\n        buyImbalance = 0;\n\n        for (uint windowInd = 0; windowInd \u003c SLIDING_WINDOW_SIZE; windowInd++) {\n            TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);\n\n            if (perBlockData.lastBlock \u003c= endBlock \u0026\u0026 perBlockData.lastBlock \u003e= startBlock) {\n                buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance);\n            }\n        }\n    }\n\n    function getImbalanceSinceRateUpdate(ERC20 token, uint rateUpdateBlock, uint currentBlock)\n        internal view\n        returns(int buyImbalance, int currentBlockImbalance)\n    {\n        buyImbalance = 0;\n        currentBlockImbalance = 0;\n        uint latestBlock = 0;\n        int imbalanceInRange = 0;\n        uint startBlock = rateUpdateBlock;\n        uint endBlock = currentBlock;\n\n        for (uint windowInd = 0; windowInd \u003c SLIDING_WINDOW_SIZE; windowInd++) {\n            TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);\n\n            if (perBlockData.lastBlock \u003c= endBlock \u0026\u0026 perBlockData.lastBlock \u003e= startBlock) {\n                imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance;\n            }\n\n            if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue;\n            if (perBlockData.lastBlock \u003c latestBlock) continue;\n\n            latestBlock = perBlockData.lastBlock;\n            buyImbalance = perBlockData.totalBuyUnitsImbalance;\n            if (uint(perBlockData.lastBlock) == currentBlock) {\n                currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance;\n            }\n        }\n\n        if (buyImbalance == 0) {\n            buyImbalance = imbalanceInRange;\n        }\n    }\n\n    function getImbalance(ERC20 token, uint rateUpdateBlock, uint currentBlock)\n        internal view\n        returns(int totalImbalance, int currentBlockImbalance)\n    {\n\n        int resolution = int(tokenControlInfo[token].minimalRecordResolution);\n\n        (totalImbalance, currentBlockImbalance) =\n            getImbalanceSinceRateUpdate(\n                token,\n                rateUpdateBlock,\n                currentBlock);\n\n        totalImbalance *= resolution;\n        currentBlockImbalance *= resolution;\n    }\n\n    function getMaxPerBlockImbalance(ERC20 token) internal view returns(uint) {\n        return tokenControlInfo[token].maxPerBlockImbalance;\n    }\n\n    function getMaxTotalImbalance(ERC20 token) internal view returns(uint) {\n        return tokenControlInfo[token].maxTotalImbalance;\n    }\n\n    function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) {\n        // check for overflows\n        require(data.lastBlockBuyUnitsImbalance \u003c int(POW_2_64 / 2));\n        require(data.lastBlockBuyUnitsImbalance \u003e int(-1 * int(POW_2_64) / 2));\n        require(data.lastBlock \u003c POW_2_64);\n        require(data.totalBuyUnitsImbalance \u003c int(POW_2_64 / 2));\n        require(data.totalBuyUnitsImbalance \u003e int(-1 * int(POW_2_64) / 2));\n        require(data.lastRateUpdateBlock \u003c POW_2_64);\n\n        // do encoding\n        uint result = uint(data.lastBlockBuyUnitsImbalance) \u0026 (POW_2_64 - 1);\n        result |= data.lastBlock * POW_2_64;\n        result |= (uint(data.totalBuyUnitsImbalance) \u0026 (POW_2_64 - 1)) * POW_2_64 * POW_2_64;\n        result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64;\n\n        return result;\n    }\n\n    function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) {\n        TokenImbalanceData memory data;\n\n        data.lastBlockBuyUnitsImbalance = int(int64(input \u0026 (POW_2_64 - 1)));\n        data.lastBlock = uint(uint64((input / POW_2_64) \u0026 (POW_2_64 - 1)));\n        data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) \u0026 (POW_2_64 - 1)));\n        data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64))));\n\n        return data;\n    }\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}