Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x05593240...10FcfE25A | |||||
0x76043f4D...d7eABf586 |
10.347553612455071975 Eth
Nonce: 269546
|
10.346595532455071975 Eth
Nonce: 269547
| 0.00095808 | ||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 571.679541757839669986 Eth | 571.680499837839669986 Eth | 0.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"}}