ETH Price: $2,428.14 (+0.34%)

Transaction Decoder

Block:
7732056 at May-10-2019 09:36:27 AM +UTC
Transaction Fee:
0.01174346 ETH $28.51
Gas Used:
2,348,692 Gas / 5 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
2,264.661009293885379329 Eth2,264.672752753885379329 Eth0.01174346
0x6E4843E9...3762D5100
0.025632440576514508 Eth
Nonce: 10
0.013888980576514508 Eth
Nonce: 11
0.01174346
0xA467b88B...44a9d26F4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 198171750132503731427277948447924979611762189773903478975118105133499420340594593555229815239548804526927084678837827736369461656612280089284037861932991347750250522860727669073128953213292640485342563992332216661548708150818297717741836286713598034081347634883377677668355214796613728115768596690385148020666259282017216952778200520512701285770332413592703963978206047388039030152226723069299471500210140549768762569139977716664250189839495089037199227768312166487549206352189678666120970340943663877421533264131369444460382495020402001048074762490318680211904367399453657271769316987748883975003227729994221641100773004340250587544477557842652389504172437013741347551422316843311965380269582812345868474016314570415211693836648635374047194605932568378338119714196482095864278836939658209217620859972762742163132584174913828938273971081118194186417027624967891319988079403980850853806848063501585269646607233239912051629555305133802310149606895752617074998203215909641510643877564836558980215606187053671973769888318313104887059385502741936846983315099824017611051166381519278632455190566274254404289527348910321795245661775876143004342575688935371987482374047026612390170947926772633095845305068982407087659791088824040038034041828458701072399982995382205771408009062917462043140210606731217526999849483021787140823539949717759587342394658213249383566484200421110109698786549414057413070881593867712975042388487534505464702235011137519623722884388216054066086979381785459585312241360459416040437718224715170993930598539320349799087768524154687743323422074894264073194544404665214176959225687581864635055678902769529562919262995360416910656314937969410604250104194491740217729194088504866124772137140464574559980852894878725871332134579676067192267210511233580554065390673235804078596044673294258789006146220721717070733401310657341631955314434525012606351974801112164660634038167645852434981361542226563449650363933680472959214535307958912196618207886866395583829381495595820800246380659265832717571214986904793108060577973065425536678018331753739061309233907168695031224784624196298569253132586343973043422765283455747207343121724747732535219932777048310282869206844547867491363314123739220385010497379950601651796555594727123360857359551257721236233868486262102272173459584081318353133883865473641501784768477364867806052950897081165257226494997226652055613778097422811295324133447055800475524652852579549334893879925774793223083467996774484015311636456992719113781207435773816992385987296408308365628573449077510815843920847878493519109843611480942677872918272150052507052877784535968496087986139303245537342587086005286570597361158304300184188062637753388314329496758264245920635126963786962144496570703747690415819615876386048010338781227238737468219603927195267536200445851330688493213089750415162902762969705081794926605524726185167293826563849549373423397198753881346066030647272225312151359209555843080949468489234365536148368619248145854485632238752083951585506197334136365879849676591434956905837761425723234349370272609404491092613869026122602181161150279786599204669670189819721178779084779434060327429288900161408509497196756168192973684782318206928923717900122808902529533924099271103284923934232468214921956048946768603832202920997099164903723314993569315604995821877464864270196232080756593102294114005292103195817963808018345994520454573520096606557278574050976124873473160364107430531175732122844611946760041241362063287196254743307405719832229718340073262967254398990030013030941198298489388339940257363887162259079938280691708738213009631204837186290066749422538103673741978876434323813884899681781550596011167469054444334096986859453510214425238497808513884818257382154346144233158147971936417379963376892639678062049623493801320678090399888764713568152718911825144123856616284320962264751052493053646615691912418853609390224842439247693301657793639853652243182488678840458485060989822368143678900774798787099692436717110759083511676937089414905230074750038422153033887557755472388993748017584896417870878681535737174962422884498167753277699906894863971211623480623151813152242396518878146631948283549060452885867979699419746046610908052154416208511075799862132303488373741517992139701048581225514190966992324749098707965852735712655329009370770842609600160337655624172141250407396408526440463231890215921327259258035115665101082993423677642745809774850979461170583050391085711951355444730799400696610554893785280034072398223083205510125840313005570481542735917488223646311815747154406149464712372353051358355545676283002221063599272310140987962162106467010143456439086296439525849855507585599208257550554006829203747260398465191518515889507888765801482700088570897467925816487868724252964036455660049172248717586458724543760410872429638509379029232124143486229609100605075379659334869407141829760605063082521850243950961967486933493799978196035511301608255188161593693708877532409498559902878003929633846046658337252056841846655624820218811926377382050318406569349660441696416435999274758021426105861286118078152184287808716658588415311723619879072351557937659217257461186989192155972551320564288862129233201163001465972089170053509270073668063797939699339116831896459901913230682477001142497521314205204604020938983780957490370183742599714341346831764935243848386144515695959036958482564772411105075977255117393681980876815443256038835825655238707464034941878105146634656711332775571816824654678210879036132660753246488215961130381744897340670239164422993724970053819865579012968084742787394511519856536811739686164966385546744495888133307931230245039040623434692854760943026500634644705213256537698533990789384462680298869339387505491392614366600479869909094732807224865155247967502923278353589131997182674925160447580713933323450340482248928161171762506566805279490197804102973339112370134823216990395406897250617477518997668102521877005557219458654326602134828799739090280690281772464196202467615103174168123066198234664698493638055824403368165075614861947065656683532822904884835590007593489001895118384770985398076680062696961409609961724428842061253364768068395359352085998725100155857992058562334146718482175956167757583855063153194355395076952935341984264429399209474502493228012550854208570079788086734284706543485917906856097467952016585899336907685309126890599777119557169769343970474689912228319961006896047904090451344451735227985597461796245885786282188094063477161706798501221951374783859213795027606789142630239424942859647990032756692910111767991955433191906098221387088849255216096645837332169552970252932500130712983915160737336349064289719154233859469386101973626457927893677761986329328238559838532536603544382701858458054877134214702429695784176554015820512634781010131484960791570349463653428082012921638592572040241041004716741876073864236721002708642944991068579830019018639678511453336112301190379753529605759793923242215315374704299023177712388091796556475227656008192548691543960023997975755748025220815820102693455852629825538418290901491261108612418470744044991850035515350291317788742448000743984147241752044553018974785823758186665078422915068565772509378537831274942845028684719344299527803901305725114269938396812838960835739150523075946848260840109239087036948162694063054074956053827280165242166905222248485967253315731671578723974332902606745451919508546328400571547700448086297136043073054568112723403234471836732860865469417804077549656907131372803201482597635571020053992447074919334979112774874316256017731671056107229369429764579575725492541107713593922679278048232197302362487277892202733246030762506670390653854767266467687118063575653031758267004302792872589119572354740570840879515300908615994114525594077816981502407793548705179791599192524202205431790030172426188180529452499459354371324430247117584293186871166603032795191094652125959764922227516624879061709998616265912517829266650674613220892861146300366320659465850981024827113436780659907614716304118636712080492986919448693228653324352147713334781949160753515704420026566017267883585570334537225814341796202944433327112813735703376240201094316443377132172322242661815838188651848752901135988176242631266650097268818720709650452589236857481881743149240966284334785114925148415393216031492089359973652465520242651801696749957733841628013768914294874926613591278324528477883991020918980780021784843506890457609845309903614894034191932248135160334195847193765990575576315535182850168150458057974361738105094556410386546096792924335026052144434065120672340056714958998346010339442591679925969598632114733823311020070497778830634116059211647394354256944912562827511575074297927041628041287658199006683411748949520111503003142925381238512860901303217600177262219877916235017275071808710473301061906372024113767804402968790726963767546925559503912490463772809231511029217395504181892745702954192247049488475198683742514831924609822341512375622953241030647590657373361265003876009953744482430636050377121955872164112522141940915839183039944832919765664239304214399732998870027169586298308340948022721790251598218682870074445032027965311495221441657844970464344591817327672546991105196675698462742981487927354017390145692798656241222757803986546692541138508116448323189879787179828852030254813725017952295236473806574502270575342332466749356482484942708778368555095660386107429472747092069189348514821673315279800070015369293264837228773303507758802735002881622779776030604051017849132928627783875502830230988882672824634446431337036538732495031881605257607470031705477871672181140273069057999000445730845127751695759422663729336049693068714216994512745440290981664271346593451600271247734280981067007703559387765555166636546171960558532720205098615423391272474187753182596766781867882052705264666631514512489759018748728896928402636732359646235490688668830445298534910518561265984745483685411367290833382002915522961415774969797632939339932344405452457405138330218993134652226574262341855350787745015984861098214722923591960195409504222168017720017335225755129385764084338557972334843716443796681124748262007606761797630187350423968854562085193548499924014185610215968562589485627492656688760623847831194651049183965154763637093472672584898263998739220097064483291794620137320473106041945175954815953039013967099277883162591488489358519919485004509630245821086327861673288888935062812444120365436455222228810207541841317935297341601989949216560271469670181042097271351566388648314614799722074288291301046770597701818012915642230344676565442228379446709672936668424170835418813347459018203682132710721947333611245905177187025323778082540159769964679311530300362479090206460163244305412228347163058347859701585042935952853292386911024588134964286261894072105706058700893176408458509130479473517633386292370491010151367417445679997445090043587329156941959564531632348629886292817179591082161903034153991166083447034202538136041409713681112468006331423593260134965732542428814846845674068152514072164978864705203935562055280716466426596449880765878686460200455152839278865568483578743031067747072835924573380205583481522401720447890975158903342392838567931981275670057354329413221252313370010555652314864301001829588991153783125749776776430950385310936494282391919319148413845397134450278710625768355391255506909864880410506714864049136401156104481518000987608216672524369032775308994920310954152905961854210307594219633484048423716216764803535161068764465625262589571926721314301340428726747561791228984160265749152839514230072210748008713927459056743591885624510598278783568226033182395825872483914041100862461240341307043836566291078191573483047074751522775861578216646275628800772865716020457523004721509686123774938302038374293615328435145682727380222085795041862056529084705623727963054850281322757545425016588812589327731615711789712976603248259006637320456402598165324597409477228058383898965258817495083273917866580218168371554437833155631270258839250319194762306972823582809361039882694535138741114285672861907566969290333092828447859324304569709536043556555929440285194581738727703328722562402458872474863637894874572734046183928156285601660643684270592608119141382524427344829552690534902921357193700580609109258585814548889775899887278271138448696934099548729361349232028177374045805011187260857272562305272540029975643810874687241505780350887748027200466052655349819788008683545779237326271933452695649550536208127015125915022991044600797472915847193552489483931119585351006593217610291985384988646658873687158692725166254662105232778433407184722121091584260705561560204463677492746876822997526504396719079002117529189124033050490322888814472120124744637232365543925951033937053439403548141595341784944418916345747967449803419171395385444735149164968176770807822061438910896392167738288134530351733108712847975567922519803292508633369537975263398404395436481236144489368515713024842029180946016386729686300357515857525974591181556676756152098701419305136224318801077114354332385651090240238398819274026961356312447090915195911569202694986777022321832880463176104172040580205782364122638351749063516405016114017546934425912286563823804271710815924866074392127624942845451001128227540985077124620661603456557153352337285724405552556431246157160102583610934415830657348001748172484175975309575096325449527629767946871430264803203596107150015725713546026226980046938761956960789428979510678115007175646319481603735669974690643906751366396733705304445246772804267574477892270968726861239296126179808215623052058362641977021367054360544042064811108675813865795523370064743583823790010850438722099946337012555291519877438425840621648502274047889457705565821298599040414915552541595441144133342100310089602398831260587939175616178265173233386832537183265192693662533116934137484386590877174951166422489003099879560453161233342070637834625612351175651995150708741045259589710368395855098118254734951496325720630220569695725614940901775254562357328129522340976733375686514433257298199673487505363069047939107238652671991369260062535377842158225776315631674864459009791441177904986815880923444899725377403705440024630145311404059365586531553966644508042490826677650055337228535287313298890477953823099088514581643470831476290048487585520302870101173638497064138119833462259561651120627791367425760549535938924611426749835148669172690826247314841592345823959818704037667538051844141030272762706326604517311269762792986287659252742565535124105850379856738058114583308799989437111853867823048178673577058452771646991734750951725250532533579035357555288367768468858109028901584671772809498507121390590220065247472459133216814796850931514039580141290112232940197425770334463188177369771714253095065190028381435945361037661805072472490220054574566103712114013051954007907629178153174890128999914226856868804174491776850827006426837875936404794333285795576377263361522958488336584228369950295076263042423163876069329435647354278872815874629986487615134763078324553420606442840480694202721963383544825785073177855076658591923267571832618500291523031516050821411230284100148796122180194813835112429252791960126560418869907871505606822488260888504297174897561590725586421051274839326769541945015975088399297882681974639713654914250184225064429228341062387089262477956056238212135560035048430356625660174028380291910827589596311066170374855495915160044763282112193053199008442187984769966583047556993433791700729128493919839213480225900086346382552590331269288349050548744027665018274121668444694682388720474991761499351121032545036133621485602576977096386829529917950474467352649213099489133686370262947791794482891036802207744598988468587019076370192468338336348021563304882360940575045952439110467209510311877236781293245787116966967113518438384673959725860709557524834294667935834191858967078181410793196985518755363319224620898314683353825827546483353460142157492491246045798134449488335200762426874462482331727850234717730841357438813030102698023063166370514067223742647649746256271571109266835536204150349223259096154709021357462949784831099589728292338225021527987701520530006700518129638422531032964171156577281993609860432300563965899663823380820043642610590156861526283301947792059029974106890142265176704450587422946694539307576930234791847552548165405025789147959601504282899606569707148572838256639655785879559025538018826063013357133382487679070052706282187005096808491581077471382426364515207360795407652352784476991005323655926115645507935350795570572889022177268047203656677929037164683791100919791476211301866669584992729936857974231350152266258328005661379406065958675503085838711183741469502861566360582421225073567486643275107191793500214787238984759116032147789204883956525541413495203770183837497761220617910149397783623249166923875793729250011794529495532111299953522958673438428479855102121984714861307154032586652465505554305588118148057019166200994121521211341462109635313420260745022359084139183612859404224042840172430987979205535374437037626881269824576000853495521371887935328484189322961728785367194572258462793797466781251795037446255227350669401789921349725524335414295010853930700793673062283209585142519624899902659048682902545671822584026128420683637512188045226446355258205321280215228662308942502782101580125925749356629042649267549141163859498048239367781106349424938230358206176137012387422027657053793287222987146512323553412194702436075852644986364810591174371720443880219212276136299418474482209091165321149013298204569548968384455152946221779330191123610455306210317362597014399345278278306940870333078380508103990034062510785028189064976689481698598556320600602171699279613001268464494494281910763838070278244152875922886095449998344212952808575412699540719335280901790120263319590053566617868142025571618715440309718899092080635217978048655736225957792431767630459084588546895439162320347610340323259181803126068652791495464180745302674340838623094572808539461611163463149204247380027303047357478258763903306356208153890298515667506531589508051472581936008534816935403705107526094132149351100026697356400084943296003425912521903884961127331252344208060493647021844339317449002331402698674670462856785691511527461392850809811257925542088188151774378824942668177638891091200735762337932640766493777159876480224222562024638033376605856906731405601538849634533093757269047795620062677120629049564499588128793123189428114761239431793908076628430496598267765961045735385488814485230827822780530555462734568952311677321449802901812639610984942680522297013370370882587419550737778520672969253175989968899873229732997578142937612931977330281737047952908816852282176173644348464383303499315660351447400664113749136772985068785030239430819280474916761583360999331973625275289332796389049579236502692584352530817354098943376156741972594103471868093201122824872728357512726934238673398690523097644309769014418460135955534181326249902584198955190618567562673376676538176428295872034253573079762220911094353921446746505098274140123926801249336112385019644925068063706312884217352579161461044292928933781465640182242678881545805013559720749553994606358787515621405008038729051917244767049571536265554885315723655407107722358399562323761215654068621623112732061536368124394357103196074118727800807379787632292027930736722726519674620293157991863094743694361430710542189315561682804143775257296610012535084927562869734807703599005184316969276443066771134886424843263531697012388378532183367806027023468860421078408619444440235748506061556524257628290177501183612487994647722870711504828407585060279650513534688862657141272534095998298693066303746105340539239326322901221482898483578737372275647907097225576460802636167344557162826438454553023223131830250371792358893510404748693306118486630572614329587548969223566457479833494382695710521540039505633527661699147784726959930547213636322852887954200238646954496189189825756557978504768902464408629554064374306506489726178422313858942673700714149321196463921990903903078695752347673840547818337079453225267060198397577695370035838258521109423024913457121672114889589670451639545169967509086276781180048938890240259943391004489258928625280209539621069770045016045901363970236608617007814979136389161

Execution Trace

KyberReserve.60606040( )
pragma solidity 0.4.18;

// File: contracts/ERC20Interface.sol

// https://github.com/ethereum/EIPs/issues/20
interface ERC20 {
    function totalSupply() public view returns (uint supply);
    function balanceOf(address _owner) public view returns (uint balance);
    function transfer(address _to, uint _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint _value) public returns (bool success);
    function approve(address _spender, uint _value) public returns (bool success);
    function allowance(address _owner, address _spender) public view returns (uint remaining);
    function decimals() public view returns(uint digits);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}

// File: contracts/Utils.sol

/// @title Kyber constants contract
contract Utils {

    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
    uint  constant internal PRECISION = (10**18);
    uint  constant internal MAX_QTY   = (10**28); // 10B tokens
    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
    uint  constant internal MAX_DECIMALS = 18;
    uint  constant internal ETH_DECIMALS = 18;
    mapping(address=>uint) internal decimals;

    function setDecimals(ERC20 token) internal {
        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
        else decimals[token] = token.decimals();
    }

    function getDecimals(ERC20 token) internal view returns(uint) {
        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
        uint tokenDecimals = decimals[token];
        // technically, there might be token with decimals 0
        // moreover, very possible that old tokens have decimals 0
        // these tokens will just have higher gas fees.
        if(tokenDecimals == 0) return token.decimals();

        return tokenDecimals;
    }

    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
        require(srcQty <= MAX_QTY);
        require(rate <= MAX_RATE);

        if (dstDecimals >= srcDecimals) {
            require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
        } else {
            require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
        }
    }

    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
        require(dstQty <= MAX_QTY);
        require(rate <= MAX_RATE);
        
        //source quantity is rounded up. to avoid dest quantity being too low.
        uint numerator;
        uint denominator;
        if (srcDecimals >= dstDecimals) {
            require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
            denominator = rate;
        } else {
            require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
            numerator = (PRECISION * dstQty);
            denominator = (rate * (10**(dstDecimals - srcDecimals)));
        }
        return (numerator + denominator - 1) / denominator; //avoid rounding down errors
    }
}

// File: contracts/PermissionGroups.sol

contract PermissionGroups {

    address public admin;
    address public pendingAdmin;
    mapping(address=>bool) internal operators;
    mapping(address=>bool) internal alerters;
    address[] internal operatorsGroup;
    address[] internal alertersGroup;
    uint constant internal MAX_GROUP_SIZE = 50;

    function PermissionGroups() public {
        admin = msg.sender;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    modifier onlyOperator() {
        require(operators[msg.sender]);
        _;
    }

    modifier onlyAlerter() {
        require(alerters[msg.sender]);
        _;
    }

    function getOperators () external view returns(address[]) {
        return operatorsGroup;
    }

    function getAlerters () external view returns(address[]) {
        return alertersGroup;
    }

    event TransferAdminPending(address pendingAdmin);

    /**
     * @dev Allows the current admin to set the pendingAdmin address.
     * @param newAdmin The address to transfer ownership to.
     */
    function transferAdmin(address newAdmin) public onlyAdmin {
        require(newAdmin != address(0));
        TransferAdminPending(pendingAdmin);
        pendingAdmin = newAdmin;
    }

    /**
     * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
     * @param newAdmin The address to transfer ownership to.
     */
    function transferAdminQuickly(address newAdmin) public onlyAdmin {
        require(newAdmin != address(0));
        TransferAdminPending(newAdmin);
        AdminClaimed(newAdmin, admin);
        admin = newAdmin;
    }

    event AdminClaimed( address newAdmin, address previousAdmin);

    /**
     * @dev Allows the pendingAdmin address to finalize the change admin process.
     */
    function claimAdmin() public {
        require(pendingAdmin == msg.sender);
        AdminClaimed(pendingAdmin, admin);
        admin = pendingAdmin;
        pendingAdmin = address(0);
    }

    event AlerterAdded (address newAlerter, bool isAdd);

    function addAlerter(address newAlerter) public onlyAdmin {
        require(!alerters[newAlerter]); // prevent duplicates.
        require(alertersGroup.length < MAX_GROUP_SIZE);

        AlerterAdded(newAlerter, true);
        alerters[newAlerter] = true;
        alertersGroup.push(newAlerter);
    }

    function removeAlerter (address alerter) public onlyAdmin {
        require(alerters[alerter]);
        alerters[alerter] = false;

        for (uint i = 0; i < alertersGroup.length; ++i) {
            if (alertersGroup[i] == alerter) {
                alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                alertersGroup.length--;
                AlerterAdded(alerter, false);
                break;
            }
        }
    }

    event OperatorAdded(address newOperator, bool isAdd);

    function addOperator(address newOperator) public onlyAdmin {
        require(!operators[newOperator]); // prevent duplicates.
        require(operatorsGroup.length < MAX_GROUP_SIZE);

        OperatorAdded(newOperator, true);
        operators[newOperator] = true;
        operatorsGroup.push(newOperator);
    }

    function removeOperator (address operator) public onlyAdmin {
        require(operators[operator]);
        operators[operator] = false;

        for (uint i = 0; i < operatorsGroup.length; ++i) {
            if (operatorsGroup[i] == operator) {
                operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                operatorsGroup.length -= 1;
                OperatorAdded(operator, false);
                break;
            }
        }
    }
}

// File: contracts/Withdrawable.sol

/**
 * @title Contracts that should be able to recover tokens or ethers
 * @author Ilan Doron
 * @dev This allows to recover any tokens or Ethers received in a contract.
 * This will prevent any accidental loss of tokens.
 */
contract Withdrawable is PermissionGroups {

    event TokenWithdraw(ERC20 token, uint amount, address sendTo);

    /**
     * @dev Withdraw all ERC20 compatible tokens
     * @param token ERC20 The address of the token contract
     */
    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
        require(token.transfer(sendTo, amount));
        TokenWithdraw(token, amount, sendTo);
    }

    event EtherWithdraw(uint amount, address sendTo);

    /**
     * @dev Withdraw Ethers
     */
    function withdrawEther(uint amount, address sendTo) external onlyAdmin {
        sendTo.transfer(amount);
        EtherWithdraw(amount, sendTo);
    }
}

// File: contracts/ConversionRatesInterface.sol

interface ConversionRatesInterface {

    function recordImbalance(
        ERC20 token,
        int buyAmount,
        uint rateUpdateBlock,
        uint currentBlock
    )
        public;

    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}

// File: contracts/SanityRatesInterface.sol

interface SanityRatesInterface {
    function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
}

// File: contracts/KyberReserveInterface.sol

/// @title Kyber Reserve contract
interface KyberReserveInterface {

    function trade(
        ERC20 srcToken,
        uint srcAmount,
        ERC20 destToken,
        address destAddress,
        uint conversionRate,
        bool validate
    )
        public
        payable
        returns(bool);

    function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
}

// File: contracts/KyberReserve.sol

/// @title Kyber Reserve contract
contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {

    address public kyberNetwork;
    bool public tradeEnabled;
    ConversionRatesInterface public conversionRatesContract;
    SanityRatesInterface public sanityRatesContract;
    mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
    mapping(address=>address) public tokenWallet;

    function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
        require(_admin != address(0));
        require(_ratesContract != address(0));
        require(_kyberNetwork != address(0));
        kyberNetwork = _kyberNetwork;
        conversionRatesContract = _ratesContract;
        admin = _admin;
        tradeEnabled = true;
    }

    event DepositToken(ERC20 token, uint amount);

    function() public payable {
        DepositToken(ETH_TOKEN_ADDRESS, msg.value);
    }

    event TradeExecute(
        address indexed origin,
        address src,
        uint srcAmount,
        address destToken,
        uint destAmount,
        address destAddress
    );

    function trade(
        ERC20 srcToken,
        uint srcAmount,
        ERC20 destToken,
        address destAddress,
        uint conversionRate,
        bool validate
    )
        public
        payable
        returns(bool)
    {
        require(tradeEnabled);
        require(msg.sender == kyberNetwork);

        require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));

        return true;
    }

    event TradeEnabled(bool enable);

    function enableTrade() public onlyAdmin returns(bool) {
        tradeEnabled = true;
        TradeEnabled(true);

        return true;
    }

    function disableTrade() public onlyAlerter returns(bool) {
        tradeEnabled = false;
        TradeEnabled(false);

        return true;
    }

    event WithdrawAddressApproved(ERC20 token, address addr, bool approve);

    function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
        approvedWithdrawAddresses[keccak256(token, addr)] = approve;
        WithdrawAddressApproved(token, addr, approve);

        setDecimals(token);
        if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) {
            tokenWallet[token] = this; // by default
            require(token.approve(this, 2 ** 255));
        }
    }

    event NewTokenWallet(ERC20 token, address wallet);

    function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {
        require(wallet != address(0x0));
        tokenWallet[token] = wallet;
        NewTokenWallet(token, wallet);
    }

    event WithdrawFunds(ERC20 token, uint amount, address destination);

    function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
        require(approvedWithdrawAddresses[keccak256(token, destination)]);

        if (token == ETH_TOKEN_ADDRESS) {
            destination.transfer(amount);
        } else {
            require(token.transferFrom(tokenWallet[token], destination, amount));
        }

        WithdrawFunds(token, amount, destination);

        return true;
    }

    event SetContractAddresses(address network, address rate, address sanity);

    function setContracts(
        address _kyberNetwork,
        ConversionRatesInterface _conversionRates,
        SanityRatesInterface _sanityRates
    )
        public
        onlyAdmin
    {
        require(_kyberNetwork != address(0));
        require(_conversionRates != address(0));

        kyberNetwork = _kyberNetwork;
        conversionRatesContract = _conversionRates;
        sanityRatesContract = _sanityRates;

        SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
    }

    ////////////////////////////////////////////////////////////////////////////
    /// status functions ///////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    function getBalance(ERC20 token) public view returns(uint) {
        if (token == ETH_TOKEN_ADDRESS)
            return this.balance;
        else {
            address wallet = tokenWallet[token];
            uint balanceOfWallet = token.balanceOf(wallet);
            uint allowanceOfWallet = token.allowance(wallet, this);

            return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;
        }
    }

    function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
        uint dstDecimals = getDecimals(dest);
        uint srcDecimals = getDecimals(src);

        return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
    }

    function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
        uint dstDecimals = getDecimals(dest);
        uint srcDecimals = getDecimals(src);

        return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
    }

    function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
        ERC20 token;
        bool  isBuy;

        if (!tradeEnabled) return 0;

        if (ETH_TOKEN_ADDRESS == src) {
            isBuy = true;
            token = dest;
        } else if (ETH_TOKEN_ADDRESS == dest) {
            isBuy = false;
            token = src;
        } else {
            return 0; // pair is not listed
        }

        uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);
        uint destQty = getDestQty(src, dest, srcQty, rate);

        if (getBalance(dest) < destQty) return 0;

        if (sanityRatesContract != address(0)) {
            uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
            if (rate > sanityRate) return 0;
        }

        return rate;
    }

    /// @dev do a trade
    /// @param srcToken Src token
    /// @param srcAmount Amount of src token
    /// @param destToken Destination token
    /// @param destAddress Destination address to send tokens to
    /// @param validate If true, additional validations are applicable
    /// @return true iff trade is successful
    function doTrade(
        ERC20 srcToken,
        uint srcAmount,
        ERC20 destToken,
        address destAddress,
        uint conversionRate,
        bool validate
    )
        internal
        returns(bool)
    {
        // can skip validation if done at kyber network level
        if (validate) {
            require(conversionRate > 0);
            if (srcToken == ETH_TOKEN_ADDRESS)
                require(msg.value == srcAmount);
            else
                require(msg.value == 0);
        }

        uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
        // sanity check
        require(destAmount > 0);

        // add to imbalance
        ERC20 token;
        int tradeAmount;
        if (srcToken == ETH_TOKEN_ADDRESS) {
            tradeAmount = int(destAmount);
            token = destToken;
        } else {
            tradeAmount = -1 * int(srcAmount);
            token = srcToken;
        }

        conversionRatesContract.recordImbalance(
            token,
            tradeAmount,
            0,
            block.number
        );

        // collect src tokens
        if (srcToken != ETH_TOKEN_ADDRESS) {
            require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));
        }

        // send dest tokens
        if (destToken == ETH_TOKEN_ADDRESS) {
            destAddress.transfer(destAmount);
        } else {
            require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));
        }

        TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);

        return true;
    }
}