Address Contract Partially Verified
Address
0x37417B2238AA52D0DD2D6252d989E728e8f706e4
Balance
0 ETH
Nonce
1
Code Size
24530 bytes
Creator
0xC9332fdC...38BC at tx 0xe0db5ab1...519942
Indexed Transactions
0
Contract Bytecode
24530 bytes
0x6003361161000c576122a3565b60003560e01c34615e405763e9333fab81186100835760243610615e40576004358060a01c615e405761010052600154615e4057610100516001556020615e52600039600051604052610100516060526100646122a9565b6020615e92600039600051604052610100516060526100816122a9565b005b63c661065781186100ca5760243610615e405760206020615e526000396000516040526020615e92600039600051606052604060043560018111615e405760051b81019050f35b6386fc88d381186100ef5760043610615e405760206100ea610180612596565b610180f35b6377c3459481186101325760043610615e4057600254610110610180612596565b61018060208101905051808281188284110218905090506101c05260206101c0f35b63095a0fc681186101555760043610615e40576020610151604061266c565b6040f35b63a7db79a581186101785760043610615e4057602061017460606126d7565b6060f35b637c1bbd8381186101b15760243610615e4057602060043560018101818112615e40579050610180526101ac610220612a52565b610220f35b63c32bd03c81186101dd5760243610615e40576020600435610180526101d8610220612a52565b610220f35b632eb858e781186102085760243610615e40576020600435606052610203610180612712565b610180f35b6324299b7a81186102405760243610615e4057602060043560018101818112615e4057905060605261023b610180612712565b610180f35b63f2388acb81186102aa5760043610615e4057600054600214615e405760075461028052602061028051610180526010610280516020526000526040600020546101a0526011610280516020526000526040600020546101c0526102a56102a0612d13565b6102a0f35b63b461100d81186102eb5760243610615e40576004358060a01c615e405760c052600054600214615e4057604060c0516040526102e760e0612fd8565b60e0f35b63ec65470681186103b15760243610615e4057600054600214615e40576007546040526000610400905b80606052604051600435136103585760106040516020526000526040600020541561034a5750506000608052602060806103af565b600160405103604052610388565b60116040516020526000526040600020541561037e5750506000608052602060806103af565b6001604051016040525b60043560405118610398576103a3565b600101818118610315575b50506001606052602060605bf35b63c16ef26481186104515760043610615e4057600054600214615e40576007546040526040516060526008546080526000610400905b8060a0526080516060511215610410576040516104008103818113615e40579050606052610449565b60106060516020526000526040600020541561042b57610449565b60605160018103818113615e405790506060526001018181186103e7575b505060206060f35b63e8dd1ef1811861049e5760243610615e40576004358060a01c615e4057604052600054600214615e40576013604051602052600052604060002060018101905054151560605260206060f35b63ab047e008118610bc85760843610615e40576004358060a01c615e405761072052600054600214615e405760026000556001543318615e40576000610740526000610da052600754611400526f7fffffffffffffffffffffffffffffff60643513615e40577fffffffffffffffffffffffffffffffff8000000000000000000000000000000160443512615e4057601454611420526000610401905b80611440526114005160443513156105625761144051156106135761140051600755610613565b601061140051602052600052604060002054610586576103ff611440511315610589565b60005b6105f357601a611460527f4465706f7369742062656c6f772063757272656e742062616e640000000000006114805261146050611460518061148001601f826000031636823750506308c379a061142052602061144052601f19601f61146051011660440161143cfd5b6114005160018103818113615e405790506114005260010181811861053b575b505060016044356064350360008112615e4057016114405260326114405111615e4057611440516024356020615eb2600039600051808202811583838304141715615e405790509050046114605260656114605110156106d357600e611480527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006114a0526114805061148051806114a001601f826000031636823750506308c379a061144052602061146052601f19601f61148051011660440161145cfd5b601361072051602052600052604060002060018101905054615e405760643560801b6044350160136107205160205260005260406000205560006032905b806114805261148051604435016114a0526064356114a0511315610734576109e5565b60106114a051602052600052604060002054156107b157600e6114c0527f42616e64206e6f7420656d7074790000000000000000000000000000000000006114e0526114c0506114c051806114e001601f826000031636823750506308c379a06114805260206114a052601f19601f6114c051011660440161149cfd5b611460516114c05261148051610815576024356020615eb2600039600051808202811583838304141715615e4057905090506114c05160016114405103808202811583838304141715615e405790509050808203828111615e4057905090506114c0525b60116114a0516020526000526040600020546114e05260126114a051602052600052604060002054611500526114e05160018101818110615e40579050611500516103e88101818110615e405790506114c051808202811583838304141715615e4057905090500461152052611520516108ef57600e611540527f416d6f756e7420746f6f206c6f770000000000000000000000000000000000006115605261154050611540518061156001601f826000031636823750506308c379a061150052602061152052601f19601f61154051011660440161151cfd5b6107405160318111615e40576001810161074052611520518160051b6107600152506115005161152051808201828110615e405790509050611500526fffffffffffffffffffffffffffffffff6115005111615e40576115005160126114a0516020526000526040600020556114e0516114c051808201828110615e4057905090506114e0526114e05160116114a05160205260005260406000205561142051156109da57610da05160318111615e405760018101610da052611500516114e051670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050048160051b610dc00152505b600101818118610711575b505060085460443580828118828412021890509050600855600954606435808281188284130218905090506009556107205160405261074051806060528060051b8060808261076060045afa505050610a3c61317d565b610a4761148061266c565b6114805160065542600555610720517f7e4f5fadb3361b33669433b392d1a203b7a236710eb272650052592e6ce62f0960606024611480376060611480a26114205115610bc1576114205163d1c923536114805260406044356114a052806114c052806114a0016000610da0518083528060051b60008260328111615e40578015610aec57905b8060051b610dc001518160051b602088010152600101818118610ace575b50508201602001915050905081015050803b15615e405760006114806106a461149c6000855af1610b22573d600060003e3d6000fd5b506114205163f9d0ca12611480526060610720516114a0526044356114c052806114e052806114a0016000610740518083528060051b60008260328111615e40578015610b8957905b8060051b61076001518160051b602088010152600101818118610b6b575b50508201602001915050905081015050803b15615e405760006114806106c461149c6000855af1610bbf573d600060003e3d6000fd5b505b6003600055005b63f3fef3a3811861126b5760443610615e40576004358060a01c615e405761076052600054600214615e405760026000556001543318615e4057670de0b6b3a764000060243511615e40576014546107805261076051604052610c2c6107e0612fd8565b6107e080516107a05260208101516107c052506107a0516107e052610760516040526107a0516060526107c051608052610c67610e6061306d565b610e6080518061080052602082018160051b80610820828460045afa50505050506108005115615e4057600060051b6108200151610d0557600b610e60527f4e6f206465706f73697473000000000000000000000000000000000000000000610e8052610e6050610e605180610e8001601f826000031636823750506308c379a0610e20526020610e4052601f19601f610e60510116604401610e3cfd5b604036610e6037600854610ea052610ea051610ec052600954610ee052610ee051610f005260006032905b80610f205260106107e051602052600052604060002054610f405260116107e051602052600052604060002054610f6052670de0b6b3a7640000602435610f205161080051811015615e405760051b6108200151808202811583838304141715615e40579050905004610f8052610f8051610f205161080051811015615e405760051b610820015103610f205161080051811015615e405760051b610820015260126107e051602052600052604060002054610fa052610fa051610f8051808203828111615e405790509050610fc052610fc05160126107e051602052600052604060002055610fa0516103e88101818110615e40579050610fa052610f405160018101818110615e40579050610f8051808202811583838304141715615e405790509050610fa0518015615e405780820490509050610fe052610fa051610f605160018101818110615e40579050610f8051808202811583838304141715615e4057905090500461100052610f4051610fe051808203828111615e405790509050610f4052610f605161100051808203828111615e405790509050610f6052610fc051610f3c57610f405115610efa57600a54610f4051808201828110615e405790509050600a555b610f605115610f3457600b54610f60516020615eb26000396000518015615e405780820490509050808201828110615e405790509050600b555b604036610f40375b610ea0516107e05118610f6f57610f4051610f6f57610f6051610f6f57610ea05160018101818112615e40579050610ea0525b610f405115610f7f576001610f86565b610f605115155b15610f94576107e051610ee0525b610f405160106107e051602052600052604060002055610f605160116107e051602052600052604060002055610e6051610fe051808201828110615e405790509050610e6052610e805161100051808201828110615e405790509050610e80526107c0516107e0511861100a5761102156611016565b60016107e051016107e0525b600101818118610d30575b5050670de0b6b3a76400006024351861105357600060136107605160205260005260406000206001810190505561107c565b6107605160405261080051806060528060051b8060808261082060045afa50505061107c61317d565b610ea051610ec0511461109157610ea0516008555b6107c051610f0051136110a657610ee0516009555b6020615e72600039600051610e605104610e60526020615eb2600039600051610e805104610e8052610760517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568610e6051610f2052610e8051610f40526040610f20a2611114610f2061266c565b610f205160065542600555610780511561124f576107805163d1c92353610f205260406000610f405280610f605280610f400160006000825260006000600060328111615e4057801561117a57905b60008160051b602087010152600101818118611163575b505081016020019050905081015050803b15615e40576000610f206106a4610f3c6000855af16111af573d600060003e3d6000fd5b506107805163f9d0ca12610f2052606061076051610f40526107a051610f605280610f805280610f40016000610800518083528060051b60008260328111615e4057801561121757905b8060051b61082001518160051b6020880101526001018181186111f9575b50508201602001915050905081015050803b15615e40576000610f206106c4610f3c6000855af161124d573d600060003e3d6000fd5b505b610e6051610f2052610e8051610f40526040610f206003600055f35b63556d6e9f81186112ae5760643610615e4057600054600214615e4057602060606004610b80376001610be0526112a3611ac0614611565b611ac0602081019050f35b63c49202e781186113565760643610615e4057600054600214615e405760606004610b80376001610be0526112e46121e0614611565b6121e08051611ac0526020810151611ae0526040810151611b00526060810151611b205260808101805180611b4052602082018160051b80611b60828460045afa50505050506106e08101516121a0526107008101516121c05250611ac0516121e052611ae0516122005260406121e0f35b6337ed3a7a81186113935760643610615e4057600054600214615e4057602060606004610b80376000610be05261138e611ac0614611565b611ac0f35b63ed7110cf811861143b5760643610615e4057600054600214615e405760606004610b80376000610be0526113c96121e0614611565b6121e08051611ac0526020810151611ae0526040810151611b00526060810151611b205260808101805180611b4052602082018160051b80611b60828460045afa50505050506106e08101516121a0526107008101516121c05250611ae0516121e052611ac0516122005260406121e0f35b635b41b90881186114585760843610615e4057336122005261147b565b63a64833a081186114b75760a43610615e40576084358060a01c615e4057612200525b600054600214615e40576002600055604060806004610b803761220051610c00526001610c20526114ad612220614969565b6122206003600055f35b63a3e346ec81186114d45760843610615e405733612200526114f7565b633c10269a81186115335760a43610615e40576084358060a01c615e4057612200525b600054600214615e40576002600055604060806004610b803761220051610c00526000610c2052611529612220614969565b6122206003600055f35b63ee4c32ee811861157f5760243610615e40576004358060a01c615e40576114a052600054600214615e405760206114a0516107605260016107805261157a6114c0615170565b6114c0f35b6362ca4b1881186115cb5760243610615e40576004358060a01c615e40576114a052600054600214615e405760206114a051610760526000610780526115c66114c0615170565b6114c0f35b63544fb5c181186116895760243610615e40576004358060a01c615e405761216052600054600214615e40576121605161076052600161078052611610612e40615b08565b612e4080518061218052602082018160051b806121a0828460045afa5050505061066081018051806127e052602082018160051b80612800828460045afa5050505050506121805115615e4057600060051b6121a00151612e40526127e05115615e4057600060051b6128000151612e60526040612e40f35b6384738380811861179c5760243610615e40576004358060a01c615e405761216052600054600214615e4057602080612e405261216051610760526000610780526116d5612180615b08565b61218081612e40016040808252808201600084518083528060051b60008260328111615e4057801561172257905b8060051b60208a0101518160051b602088010152600101818118611703575b505082016020019150509050810190508060208301526106608301818301600082518083528060051b60008260328111615e4057801561177d57905b8060051b6020880101518160051b60208801015260010181811861175e575b5050820160200191505090509050810190509050905081019050612e40f35b6348e995f98118611f0a5760243610615e4057600054600214615e4057600854610280526009546102a0526007546102c0526117d9610320612596565b61032080516102e052602081015161030052506102c0516060526117fe610340612712565b610340516103205261032051610320516102e0516fffffffffffffffffffffffffffffffff8111615e40576002810a9050046102e051808202811583838304141715615e40579050905004610340526020615f52600039600051610340516020615f32600039600051808202811583838304141715615e405790509050046103605260a036610380376032610420526001610440526000610432905b80610460526103205115615e405760106102c0516020526000526040600020546104805260116102c0516020526000526040600020546104a05261046051611913576102c05161018052610480516101a0526104a0516101c0526118ff6104c0612d13565b6104c0516004351015611913576000610440525b610480511561192357600161192a565b6104a05115155b6104c0526104c05115611a6657610480516060526104a0516080526102e05160a0526103205160c05261195e6104e0612ad3565b6104e0516103a052670de0b6b3a7640000610320516020615ef26000396000516103a051808202811583838304141715615e4057905090506102e051808202811583838304141715615e405790509050046102e051808202811583838304141715615e405790509050046103c0526102e0516020615f126000396000516103a051808202811583838304141715615e40579050905061032051808202811583838304141715615e405790509050046103e0526103c05161048051808201828110615e4057905090506103e0516104a051808201828110615e405790509050808202811583838304141715615e4057905090506104005260326104205118611a66576000610420525b6103605160043511611b98576103405160043510611b98576104c05115611e3d576103e05161040051670de0b6b3a7640000810281670de0b6b3a7640000820418615e405790506004358015615e405780820490509050604052611acb610500612333565b610500516103e05180828118828411021890509050036104e0526103c051610400516103e0516104e051808201828110615e4057905090508015615e4057808204905090506103c05180828118828411021890509050036105005261044051611b6357610380516104a0516104e0516104a0518082811882841102189050905003808201828110615e40579050905061038052611e3d565b610380516104805161050051610480518082811882841102189050905003808201828110615e40579050905061038052611e3d565b6102e05161032051670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050046104e05261044051611ce8576104c05115611c245761038051610400516103c0518015615e4057808204905090506103e051808203828111615e4057905090506104a051808203828111615e405790509050808201828110615e405790509050610380525b610280516102c05118611c3657611e3d565b60316104205118611c4657611e3d565b6020615fb26000396000516104e0511115611c6057611e3d565b6102c05160018103818113615e405790506102c05261034051610360526020615f32600039600051610340516020615f52600039600051808202811583838304141715615e40579050905004610340526020615f12600039600051610320516020615ef2600039600051808202811583838304141715615e4057905090500461032052611e1b565b6104c05115611d415761038051610400516103e0518015615e4057808204905090506103c051808203828111615e40579050905061048051808203828111615e405790509050808201828110615e405790509050610380525b6102a0516102c05118611d5357611e3d565b60316104205118611d6357611e3d565b6020615fb26000396000518015615e4057806ec097ce7bc90715b34b9f10000000000490506104e0511015611d9757611e3d565b6102c05160018101818112615e405790506102c05261036051610340526020615f52600039600051610360516020615f32600039600051808202811583838304141715615e40579050905004610360526020615ef2600039600051610320516020615f12600039600051808202811583838304141715615e40579050905004610320525b60326104205114611e325760016104205101610420525b60010181811861189a575b505061038051670de0b6b3a7640000810281670de0b6b3a7640000820418615e405790506002546103005180828118828411021890509050670de0b6b3a7640000038015615e4057808204905090506103805261038051611eb05760006104605261044051610480526040610460611f08565b61044051611ed75760016020615eb260003960005160016103805103040161038052611ef2565b60016020615e72600039600051600161038051030401610380525b6103805161046052610440516104805260406104605bf35b63d4387a998118611f965760243610615e4057600054600214615e405760026000556001543318615e4057611f3f606061266c565b606051604052604051600655426005556004356004557f52543716810f73c3fa9bca74622aecb6d3614ca4991472f3e999d531c2f6afb86004356060526040516080524260a05260606060a1602060406003600055f35b631aa02d598118611ff95760243610615e4057600054600214615e405760026000556001543318615e40576004356002557e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a760043560405260206040a16003600055005b633217902f811861205d5760243610615e4057600054600214615e405760026000556001543318615e40576004356003557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b7460043560405260206040a16003600055005b63822fe50781186120995760043610615e4057600054600214615e405760026000556001543318615e40576000600a556000600b556003600055005b63cc1891c781186120cb5760243610615e40576004358060a01c615e40576040526001543318615e4057604051601455005b63f851a44081186120ea5760043610615e405760015460405260206040f35b63f446c1d081186121115760043610615e40576020615ef260003960005160405260206040f35b63ddca3f4381186121305760043610615e405760025460405260206040f35b63fee3f7f9811861214f5760043610615e405760035460405260206040f35b632c4e722e811861216e5760043610615e405760045460405260206040f35b638f8654c5811861218d5760043610615e405760075460405260206040f35b63ca72a82181186121ac5760043610615e405760085460405260206040f35b63aaa615fc81186121cb5760043610615e405760095460405260206040f35b63d1fea73381186121ea5760043610615e4057600a5460405260206040f35b6389960ba781186122095760043610615e4057600b5460405260206040f35b635ea0e01b81186122285760043610615e4057600c5460405260206040f35b63ebcb006781186122555760243610615e4057601060043560205260005260406000205460405260206040f35b6331f7e30681186122825760243610615e4057601160043560205260005260406000205460405260206040f35b63611105d381186122a15760043610615e405760145460405260206040f35b505b60006000fd5b60405163095ea7b360805260605160a0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c052602060806044609c6000855af16122fa573d600060003e3d6000fd5b3d61231057803b15615e4057600160e052612327565b60203d10615e40576080518060011c615e405760e0525b60e090505115615e4057565b6040518060b571010000000000000000000000000000000000821061235f578160801c91508060401b90505b6901000000000000000000821061237d578160401c91508060201b90505b650100000000008210612397578160201c91508060101b90505b630100000082106123af578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050815250565b60405160605242600f54808203828111615e40579050905080607881188260781002189050607803608052600060a0526080511561258657600d5460c052600e5460e05260c051604051116124e25760c051604051670de0b6b3a7640000810281670de0b6b3a7640000820418615e405790500460a052670b1a2bc2ec4fffff60a0511161255657671158e460913d000060c051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004606052670b1a2bc2ec50000060a052612556565b60405160c051670de0b6b3a7640000810281670de0b6b3a7640000820418615e405790500460a052670b1a2bc2ec4fffff60a0511161255657670de0b6b3a764000060c051671158e460913d0000810281671158e460913d0000820418615e4057905004606052670b1a2bc2ec50000060a0525b60786080516ec097ce7bc90715b34b9f1000000000600360a0510a0460e051670de0b6b3a76400000103020460a0525b606051815260a051602082015250565b600c5463a035b1fe610100526020610100600461011c845afa6125be573d600060003e3d6000fd5b60203d10615e40576101009050516040526125da61014061241b565b61014080518252602081015160208301525050565b600c5463ceb7f759610140526020610140600461015c6000855af1612619573d600060003e3d6000fd5b60203d10615e405761014090505160405261263561018061241b565b6101808051610100526020810151610120525042600f5561010051600d5561012051600e5561010051815261012051602082015250565b670de0b6b3a764000060065460045442600554808203828111615e405790509050808202811583838304141715615e40579050905080670de0b6b3a764000001670de0b6b3a76400008110615e40579050808202811583838304141715615e40579050905004815250565b670de0b6b3a76400006020615ed26000396000516126f5604061266c565b604051808202811583838304141715615e40579050905004815250565b6060517f80000000000000000000000000000000000000000000000000000000000000008114615e40576000036020615f9260003960005180820281191515600160ff1b8414151782158484840514171615615e4057905090506080527ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c260805112615e4057680755bf798b4a1bf1e460805113615e4057670de0b6b3a764000060805160601b0560a0526c010000000000000000000000006b8000000000000000000000006bb17217f7d1cf79abc9e3b39860a05160601b05010560c0526bb17217f7d1cf79abc9e3b39860c0510260a0510360a0526c10fe68e7fd37d0007b713f765060a0510160e0526d02d16720577bd19bf614176fe9ea6c0100000000000000000000000060a05160e05102050160e0526d04a4fd9f2a8b96949216d2255a6c60a05160e0510103610100526e0587f503bb6ea29d25fcb7401964506c0100000000000000000000000060e051610100510205016101005279d835ebba824c98fb31b83b2ca45c00000000000000000000000060a0516101005102016101005260a0516c240c330e9fb2d9cbaf0fd5aafc8103818113615e40579050610120526d0277594991cfc85f6e2461837cd96c0100000000000000000000000060a05161012051020501610120526d1a521255e34f6a5061b25ef1c9c46c0100000000000000000000000060a05161012051020503610120526db1bbb201f443cf962f1a1d3db4a56c0100000000000000000000000060a05161012051020501610120526e02c72388d9f74f51a9331fed693f156c0100000000000000000000000060a05161012051020503610120526e05180bb14799ab47a8a8cb2a527d576c0100000000000000000000000060a051610120510205016101205274029d9dc38563c32e5c2f6dc192ee70ef65f9978af361012051610100510560008112615e40570260c360c051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315612a0d5781811b612a14565b81816000031c5b9050905061014052670de0b6b3a7640000612a306101606126d7565b6101605161014051808202811583838304141715615e40579050905004815250565b61018051606052612a646101c0612712565b6101c0516101a052612a776101e0612596565b6101e0516101c0526101a0516101c0516fffffffffffffffffffffffffffffffff8111615e40576002810a90506101a0518015615e4057808204905090506101c051808202811583838304141715615e40579050905004815250565b60a05115615e4057600060e05260605115612b295760a05160c0516020615f12600039600051808202811583838304141715615e405790509050606051808202811583838304141715615e4057905090500460e0525b60805115612bb65760e051670de0b6b3a76400006020615ef260003960005160a0516fffffffffffffffffffffffffffffffff8111615e40576002810a9050808202811583838304141715615e40579050905060c0518015615e405780820490509050608051808202811583838304141715615e40579050905004808201828110615e40579050905060e0525b60605115612bc8576080511515612bcb565b60005b612c0c5760a0516020615ef26000396000510260e051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004815250612d1156612d11565b60e0516fffffffffffffffffffffffffffffffff8111615e40576002810a9050670de0b6b3a76400006020615ef26000396000518060021b818160021c18615e4057905060a051808202811583838304141715615e405790509050608051808202811583838304141715615e40579050905004606051808202811583838304141715615e405790509050808201828110615e4057905090506101005260a0516020615ef26000396000518060011b818160011c18615e405790500260e05161010051604052612cdc610120612333565b61012051808201828110615e405790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050048152505b565b61018051606052612d25610200612712565b610200516101e052612d38610220612596565b61022051610200526101e05115615e40576101a051612e1a576101c051612dcf576020615f126000396000516101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615e40576002810a90500461020051808202811583838304141715615e405790509050046020615ef2600039600051808202811583838304141715615e40579050905004815250612fd6565b6101e0516101e051610200516fffffffffffffffffffffffffffffffff8111615e40576002810a90500461020051808202811583838304141715615e40579050905004815250612fd6565b6101c051612eac576020615ef26000396000516101e0516020615f12600039600051808202811583838304141715615e405790509050046101e0526101e051610200516fffffffffffffffffffffffffffffffff8111615e40576002810a90506101e0518015615e40578082049050905061020051808202811583838304141715615e40579050905004815250612fd6565b6101a0516060526101c0516080526102005160a0526101e05160c052612ed3610240612ad3565b61024051610220526101e0516020615ef260003960005161022051808202811583838304141715615e40579050905061020051808202811583838304141715615e4057905090500461020051808202811583838304141715615e40579050905061024052610200516020615f1260003960005161022051808202811583838304141715615e4057905090506101e051808202811583838304141715615e4057905090500461026052610240516101a051670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050808201828110615e405790509050610260516101c051808201828110615e4057905090508015615e4057808204905090508152505b565b6013604051602052600052604060002054606052700100000000000000000000000000000000606051056080526060517001000000000000000000000000000000008107905060a0526f8000000000000000000000000000000060a0511261305d5770010000000000000000000000000000000060a0510360a0526001608051016080525b60a0518152608051602082015250565b600060a0526080516060518082038281136000831218615e40579050905060018101818112615e4057905060008112615e40576107005260006019905b80610720526107005160a051186130c05761315d565b601360405160205260005260406000206001810190506107205160188111615e405781019050546107405260a05160318111615e40576001810160a0526fffffffffffffffffffffffffffffffff61074051168160051b60c00152506107005160a0511861312d5761315d565b60a05160318111615e40576001810160a052610740518060801c90508160051b60c00152506001018181186130aa575b505060a0518082528060051b6020830181818360c060045afa5050505050565b60006106c05260006019905b806106e0526060516106c0511061319f57613235565b6106c051606051811015615e405760051b608001516107005260016106c051016106c0526106c051606051146131f4576106c051606051811015615e405760051b608001518060801b90506107005117610700525b60016106c051016106c05261070051601360405160205260005260406000206001810190506106e05160188111615e40578101905055600101818118613189575b5050565b600854610240526009546102605261072036610280376007546102e0526102e0516060526132686109c0612712565b6109c0516109a05260106102e0516020526000526040600020546109c05260116102e0516020526000526040600020546109e0526101a051610a00526002546101e05180828118828411021890509050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610a2052600354610a40526032610a60526000610432905b80610a8052608036610aa0376109c0511561330757600161330e565b6109e05115155b15613457576032610a60511861332d576102e0516102c0526000610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c052613354610b20612ad3565b610b2051610aa052670de0b6b3a76400006020615ef2600039600051610aa051808202811583838304141715615e4057905090506101c051808202811583838304141715615e4057905090506109a0518015615e4057808204905090506101c051808202811583838304141715615e40579050905004610ac0526101c0516020615f12600039600051610aa051808202811583838304141715615e4057905090506109a051808202811583838304141715615e40579050905004610ae052610ac0516109c051808201828110615e405790509050610ae0516109e051808201828110615e405790509050808202811583838304141715615e405790509050610b00525b6032610a60511461349f576109e051610b2052610180511561347c576109c051610b20525b6103005160318111615e40576001810161030052610b20518160051b6103200152505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004610b205261018051613835576109c0511561378457610ac0511561378457610ac051610b005104610ae051808203828111615e4057905090506109e051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e40579050905004610b6052610a0051610b6051101561363057610b605160018181186001831102189050610b6052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b4052610a0051610b6051808203828111615e405790509050610a00526109e051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b610320015261028051610b6051808201828110615e405790509050610280526102a0516109c051808201828110615e4057905090506102a052610b4051610980510161098052613784565b610a2051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004610b4052610b0051610ae0516109e051610b4051808201828110615e405790509050808201828110615e4057905090508015615e405780820490509050610ac051808203828111615e40579050905060018101818110615e405790506109c0518082811882841002189050905061096052670de0b6b3a7640000610b4051610a005103610a4051808202811583838304141715615e40579050905004610b40526109e051610a0051808201828110615e4057905090506109e0526102a0516109c05161096051808203828111615e405790509050808201828110615e4057905090506102a0526109e051610b4051808203828111615e405790509050610a605161030051811015615e405760051b61032001526101a05161028052610b4051610980510161098052613bd0565b610431610a805114613bae57610240516102e051186137a257613bd0565b6031610a6051186137b257613bd0565b6020615fb2600039600051610b205111156137cc57613bd0565b6102e05160018103818113615e405790506102e0526020615f126000396000516109a0516020615ef2600039600051808202811583838304141715615e405790509050046109a05260106102e0516020526000526040600020546109c05260006109e052613bae565b6109e05115613ae757610ae05115613ae757610ae051610b005104610ac051808203828111615e4057905090506109c051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e40579050905004610b6052610a0051610b6051101561399357610b605160018181186001831102189050610b6052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b4052610a0051610b6051808203828111615e405790509050610a00526109c051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b610320015261028051610b6051808201828110615e405790509050610280526102a0516109e051808201828110615e4057905090506102a052610b4051610980510161098052613ae7565b610a2051610a0051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004610b4052610b0051610ac0516109c051610b4051808201828110615e405790509050808201828110615e4057905090508015615e405780820490509050610ae051808203828111615e40579050905060018101818110615e405790506109e0518082811882841002189050905061096052670de0b6b3a7640000610b4051610a005103610a4051808202811583838304141715615e40579050905004610b40526109c051610a0051808201828110615e4057905090506109c0526102a0516109e05161096051808203828111615e405790509050808201828110615e4057905090506102a0526109c051610b4051808203828111615e405790509050610a605161030051811015615e405760051b61032001526101a05161028052610b4051610980510161098052613bd0565b610431610a805114613bae57610260516102e05118613b0557613bd0565b6031610a605118613b1557613bd0565b6020615fb26000396000518015615e4057806ec097ce7bc90715b34b9f1000000000049050610b20511015613b4957613bd0565b6102e05160018101818112615e405790506102e0526020615ef26000396000516109a0516020615f12600039600051808202811583838304141715615e405790509050046109a05260006109c05260116102e0516020526000526040600020546109e0525b6032610a605114613bc5576001610a605101610a60525b6001018181186132eb575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526102805181526102a05160208201526102c05160408201526102e0516060820152610300518060808301528060051b6020608084010181818361032060045afa50505050610960516106e08201526109805161070082015250565b600854610240526009546102605261072036610280376007546102e0526102e051606052613c886109c0612712565b6109c0516109a05260106102e0516020526000526040600020546109c05260116102e0516020526000526040600020546109e0526101a051610a00526002546101e05180828118828411021890509050670de0b6b3a7640000036ec097ce7bc90715b34b9f100000000004610a2052600354610a40526032610a60526000610432905b80610a8052608036610aa0376109c05115613d27576001613d2e565b6109e05115155b15613e77576032610a605118613d4d576102e0516102c0526000610a60525b6109c0516060526109e0516080526101c05160a0526109a05160c052613d74610b20612ad3565b610b2051610aa052670de0b6b3a76400006020615ef2600039600051610aa051808202811583838304141715615e4057905090506101c051808202811583838304141715615e4057905090506109a0518015615e4057808204905090506101c051808202811583838304141715615e40579050905004610ac0526101c0516020615f12600039600051610aa051808202811583838304141715615e4057905090506109a051808202811583838304141715615e40579050905004610ae052610ac0516109c051808201828110615e405790509050610ae0516109e051808201828110615e405790509050808202811583838304141715615e405790509050610b00525b6032610a605114613ebf576109e051610b20526101805115613e9c576109c051610b20525b6103005160318111615e40576001810161030052610b20518160051b6103200152505b6101c0516109a051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004610b205261018051614221576109c0511561417057610ac0511561417057610a00516109c051101561404857610ac051610b005104610ae051808203828111615e4057905090506109e051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e4057905090500460018181186001831102189050610b6052610a00516109c051808203828111615e405790509050610a005261028051610b6051808201828110615e405790509050610280526102a0516109c051808201828110615e4057905090506102a052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b40526109e051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b6103200152610b4051610980510161098052614170565b610a00516109c0510361096052610b0051610ac05161096051808201828110615e4057905090508015615e405780820490509050610ae051808203828111615e4057905090506109e051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e40579050905004610b60526101a0516102a05261028051610b6051808201828110615e40579050905061028052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b40526109e051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b6103200152610b4051610980510161098052614588565b610431610a80511461456657610240516102e0511861418e57614588565b6031610a60511861419e57614588565b6020615fb2600039600051610b205111156141b857614588565b6102e05160018103818113615e405790506102e0526020615f126000396000516109a0516020615ef2600039600051808202811583838304141715615e405790509050046109a05260106102e0516020526000526040600020546109c05260006109e052614566565b6109e0511561449f57610ae0511561449f57610a00516109e051101561437757610ae051610b005104610ac051808203828111615e4057905090506109c051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e4057905090500460018181186001831102189050610b6052610a00516109e051808203828111615e405790509050610a005261028051610b6051808201828110615e405790509050610280526102a0516109e051808201828110615e4057905090506102a052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b40526109c051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b6103200152610b405161098051016109805261449f565b610a00516109e0510361096052610b0051610ae05161096051808201828110615e4057905090508015615e405780820490509050610ac051808203828111615e4057905090506109c051808203828111615e405790509050610b4052670de0b6b3a7640000610b4051610a2051808202811583838304141715615e40579050905004610b60526101a0516102a05261028051610b6051808201828110615e40579050905061028052670de0b6b3a7640000610b4051610b605103610a4051808202811583838304141715615e40579050905004610b40526109c051610b6051808201828110615e405790509050610b4051808203828111615e405790509050610a605161030051811015615e405760051b6103200152610b4051610980510161098052614588565b610431610a80511461456657610260516102e051186144bd57614588565b6031610a6051186144cd57614588565b6020615fb26000396000518015615e4057806ec097ce7bc90715b34b9f1000000000049050610b2051101561450157614588565b6102e05160018101818112615e405790506102e0526020615ef26000396000516109a0516020615f12600039600051808202811583838304141715615e405790509050046109a05260006109c05260116102e0516020526000526040600020546109e0525b6032610a60511461457d576001610a605101610a60525b600101818118613d0b575b5050610200516102005160016102005103610280510104026102805261022051610220516102a05104026102a0526102805181526102a05160208201526102c05160408201526102e0516060820152610300518060808301528060051b6020608084010181818361032060045afa50505050610960516106e08201526109805161070082015250565b610b8051614626576001610ba0511815614629565b60005b614649576001610b80511861464257610ba0511561464c565b600061464c565b60015b6146b657600b610c00527f57726f6e6720696e646578000000000000000000000000000000000000000000610c2052610c0050610c005180610c2001601f826000031636823750506308c379a0610bc0526020610be052601f19601f610c00510116604401610bdcfd5b61072036610c0037610bc05161472457610c00518152610c20516020820152610c40516040820152610c60516060820152610c80518060808301528060051b60206080840101818183610ca060045afa505050506112e0516106e08201526113005161070082015250614967565b6020615eb2600039600051611320526020615e7260003960005161134052610b8051614769576020615e72600039600051611320526020615eb2600039600051611340525b6147746113a0612596565b6113a080516113605260208101516113805250610be05161484357610b80511561018052610bc05161134051808202811583838304141715615e4057905090506101a052611360516101c052611380516101e052611320516102005261134051610220526147e36113a0613c59565b6113a08051610c00526020810151610c20526040810151610c40526060810151610c605260808101805180610c8052602082018160051b80610ca0828460045afa50505050506106e08101516112e05261070081015161130052506148f3565b610b80511561018052610bc05161132051808202811583838304141715615e4057905090506101a052611360516101c052611380516101e052611320516102005261134051610220526148976113a0613239565b6113a08051610c00526020810151610c20526040810151610c40526060810151610c605260808101805180610c8052602082018160051b80610ca0828460045afa50505050506106e08101516112e05261070081015161130052505b61132051610c005104610c005261134051610c205104610c2052610c00518152610c20516020820152610c40516040820152610c60516060820152610c80518060808301528060051b60206080840101818183610ca060045afa505050506112e0516106e082015261130051610700820152505b565b610b805161497e576001610ba0511815614981565b60005b6149a1576001610b80511861499a57610ba051156149a4565b60006149a4565b60015b614a0e57600b610c40527f57726f6e6720696e646578000000000000000000000000000000000000000000610c6052610c4050610c405180610c6001601f826000031636823750506308c379a0610c00526020610c2052601f19601f610c40510116604401610c1cfd5b614a19610c806125ef565b610c808051610c40526020810151610c605250610bc051614a455760008152600060208201525061516e565b601454610c80526000610ca0526020615e52600039600051611300526020615e92600039600051611320526020615e72600039600051611340526020615eb2600039600051611360526001610b805118614ac857611360516113405261132051611300526020615e72600039600051611360526020615e52600039600051611320525b6107203661138037610c2051614b8c57610b80511561018052610bc05161136051808202811583838304141715615e4057905090506101a052610c40516101c052610c60516101e05261134051610200526113605161022052614b2c611aa0613c59565b611aa080516113805260208101516113a05260408101516113c05260608101516113e0526080810180518061140052602082018160051b80611420828460045afa50505050506106e0810151611a6052610700810151611a805250614c3c565b610b80511561018052610bc05161134051808202811583838304141715615e4057905090506101a052610c40516101c052610c60516101e05261134051610200526113605161022052614be0611aa0613239565b611aa080516113805260208101516113a05260408101516113c05260608101516113e0526080810180518061140052602082018160051b80611420828460045afa50505050506106e0810151611a6052610700810151611a8052505b611340516113805104611aa052611360516113a05104611ac052610c2051614cd657610be051611aa0511115614d4a576008611ae0527f536c697070616765000000000000000000000000000000000000000000000000611b0052611ae050611ae05180611b0001601f826000031636823750506308c379a0611aa0526020611ac052601f19601f611ae0510116604401611abcfd614d4a565b610be051611ac0511015614d4a576008611ae0527f536c697070616765000000000000000000000000000000000000000000000000611b0052611ae050611ae05180611b0001601f826000031636823750506308c379a0611aa0526020611ac052601f19601f611ae0510116604401611abcfd5b611ac051614d59576001614d5f565b611aa051155b15614d755760008152600060208201525061516e565b61134051611a805104611a8052610b8051614da757600a54611a8051808201828110615e405790509050600a55614dc0565b600b54611a8051808201828110615e405790509050600b555b611300516323b872dd611ae05233611b005230611b2052611aa051611b40526020611ae06064611afc6000855af1614dfd573d600060003e3d6000fd5b3d614e1457803b15615e40576001611b6052614e2d565b60203d10615e4057611ae0518060011c615e4057611b60525b611b6090505115615e40576113205163a9059cbb611ae052610c0051611b0052611ac051611b20526020611ae06044611afc6000855af1614e73573d600060003e3d6000fd5b3d614e8a57803b15615e40576001611b4052614ea3565b60203d10615e4057611ae0518060011c615e4057611b40525b611b4090505115615e40576113c0516113e05180828118828412021890509050611ae052611ae051611b00526113c0516113e051037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315614f065780614f15565b806000038114615e4057806000035b9050611b205260006032905b80611b4052604036611b6037610b8051614f6a57611b405161140051811015615e405760051b6114200151611b60526113e051611ae05118614fa057611a6051611b8052614fa0565b611b4051611b20510361140051811015615e405760051b6114200151611b80526113e051611ae05118614fa057611a6051611b60525b611b60516010611ae051602052600052604060002055611b80516011611ae051602052600052604060002055610c805115615041576000611ba052611b80511561501e576012611ae051602052600052604060002054611b8051670de0b6b3a7640000810281670de0b6b3a7640000820418615e4057905004611ba0525b610ca05160318111615e405760018101610ca052611ba0518160051b610cc00152505b611b2051611b40511861505357615069565b6001611ae05101611ae052600101818118614f21575b50506113e051600755610c00517fb2e76ae99761dc136e598d4a629bb347eccb9532a5f8bbd72e18467c3c34cc98610b8051611b4052611aa051611b6052610ba051611b8052611ac051611ba0526080611b40a2610c80511561515d57610c805163d1c92353611b40526040611b0051611b605280611b805280611b60016000610ca0518083528060051b60008260328111615e4057801561512557905b8060051b610cc001518160051b602088010152600101818118615107575b50508201602001915050905081015050803b15615e40576000611b406106a4611b5c6000855af161515b573d600060003e3d6000fd5b505b611aa0518152611ac0516020820152505b565b610760516040526151826107e0612fd8565b6107e080516107a05260208101516107c05250610760516040526107a0516060526107c0516080526151b5610e4061306d565b610e408051806107e052602082018160051b80610800828460045afa50505050506107e05115615e4057600060051b61080001516151f7576000815250615b06565b615202610e60612596565b610e6051610e4052610e405115615e40576107a05160018103818113615e40579050610e6052600754610e80526107a051606052615241610ec0612712565b610ec051610ea0526000610ec05260006032905b80610ee052610e605160018101818112615e40579050610e60526107c051610e6051131561528257615acc565b604036610f0037610e8051610e6051126152ad576011610e6051602052600052604060002054610f20525b610e8051610e6051136152d1576010610e6051602052600052604060002054610f00525b610ea051610f40526020615ef2600039600051610ea0516020615f12600039600051808202811583838304141715615e40579050905004610ea052610f005161532157610f205161532157615ac1565b6012610e6051602052600052604060002054610f6052610ee0516107e051811015615e405760051b6108000151610f8052610f605161535f57615ac1565b610f805161536c57615ac1565b610f60516103e88101818110615e40579050610f60526020615ef2600039600051610ea051610e40516fffffffffffffffffffffffffffffffff8111615e40576002810a9050610ea0518015615e405780820490509050610e4051808202811583838304141715615e405790509050046020615f12600039600051808202811583838304141715615e40579050905004610fa052610f005161540f576001615415565b610f2051155b1561560457610f4051610e40511161551857610ea051610e4051101561560457610f0051610fc052610f005161547057670de0b6b3a7640000610f2051610fa051808202811583838304141715615e40579050905004610fc0525b610780516154b457610ec051610f6051610fc051610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610ec051610f6051610f4051610fc0516020615f72600039600051808202811583838304141715615e40579050905004610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac156615604565b610f2051610fc052610f205161556057610f0051670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050610fa0518015615e405780820490509050610fc0525b610780516155c857610ec051610f60516020615f72600039600051610fc051610f4051808202811583838304141715615e40579050905004610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610ec051610f6051610fc051610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610f0051606052610f2051608052610e405160a052610f405160c05261562b610fe0612ad3565b610fe051610fc052670de0b6b3a7640000610f40516020615ef2600039600051610fc051808202811583838304141715615e405790509050610e4051808202811583838304141715615e40579050905004610e4051808202811583838304141715615e40579050905004610fe052610e40516020615f12600039600051610fc051808202811583838304141715615e405790509050610f4051808202811583838304141715615e4057905090500461100052610fe051610f0051808201828110615e40579050905061100051610f2051808201828110615e405790509050808202811583838304141715615e4057905090506110205260403661104037610f4051610e4051116159f257610ea051610e40511061591f5761100051610e405161102051670de0b6b3a7640000810281670de0b6b3a7640000820418615e405790500460405261577b611080612333565b6110805161100051808281188284110218905090500361106052610fe051611020516110005161106051808201828110615e4057905090508015615e405780820490509050610fe0518082811882841102189050905003611040526107805161587857610ec051610f605161104051670de0b6b3a764000061106051610ea051610e4051808202811583838304141715615e405790509050604052615821611080612333565b61108051808202811583838304141715615e40579050905004808201828110615e405790509050610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610ec051610f60516110605161104051670de0b6b3a7640000810281670de0b6b3a7640000820418615e40579050610f4051610e4051808202811583838304141715615e4057905090506040526158d0611080612333565b611080518015615e405780820490509050808201828110615e405790509050610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610fe05161102051611000518015615e405780820490509050610fe0518082811882841102189050905003611040526107805161599257610ec051610f605161104051610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610ec051610f6051610f4051611040516020615f72600039600051808202811583838304141715615e40579050905004610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b6110005161102051610fe0518015615e4057808204905090506110005180828118828411021890509050036110605261078051615a8957610ec051610f60516020615f7260003960005161106051610f4051808202811583838304141715615e40579050905004610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec052615ac1565b610ec051610f605161106051610f8051808202811583838304141715615e40579050905004808201828110615e405790509050610ec0525b600101818118615255575b505061078051615af2576020615e72600039600051610ec05104815250615b0656615b06565b6020615eb2600039600051610ec051048152505b565b60006107a0526000610e00526107805115615b5e576107a05160318111615e4057600181016107a05260008160051b6107c0015250610e005160318111615e405760018101610e005260008160051b610e200152505b61076051604052615b706114a0612fd8565b6114a080516114605260208101516114805250610760516040526114605160605261148051608052615ba3611b0061306d565b611b008051806114a052602082018160051b806114c0828460045afa50505050506114a05115615e4057600060051b6114c0015115615d8d5760006032905b80611b00526012611460516020526000526040600020546103e88101818110615e40579050611b2052611b00516114a051811015615e405760051b6114c00151611b4052611b205160106114605160205260005260406000205460018101818110615e40579050611b4051808202811583838304141715615e40579050905004611b6052611b205160116114605160205260005260406000205460018101818110615e40579050611b4051808202811583838304141715615e40579050905004611b805261078051615d0f576107a05160318111615e4057600181016107a0526020615e72600039600051611b6051048160051b6107c0015250610e005160318111615e405760018101610e00526020615eb2600039600051611b8051048160051b610e20015250615d62565b6107a05115615e4057600060051b6107c0018051611b6051808201828110615e405790509050815250610e005115615e4057600060051b610e20018051611b8051808201828110615e4057905090508152505b611480516114605118615d7457615d8a565b6001611460510161146052600101818118615be2575b50505b6107805115615dfb576020615e726000396000516107a05115615e4057600060051b6107c00151046107a05115615e4057600060051b6107c001526020615eb2600039600051610e005115615e4057600060051b610e20015104610e005115615e4057600060051b610e2001525b6107a0518082528060051b602083018181836107c060045afa50505050610e0051806106608301528060051b6020610660840101818183610e2060045afa5050505050565b600080fda165767970657283000307000b000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000006ffb98d0d49fffb6a500000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000026490000000000000000000000000000000000000000000000000df29c916c5c292b0000000000000000000000000000000000000000000000000023b4ba2a6bdbf700000000000000000000000000000000000000000000000016f031f3cb592927
Verified Source Code Partial Match
Compiler: v0.3.7+commit.6020b8bb
LLAMMA-crvUSDAMM.vy 1720 lines
# @version 0.3.7
"""
@title LLAMMA - crvUSD AMM
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
"""
# Glossary of variables and terms
# =======================
# * ticks, bands - price ranges where liquidity is deposited
# * x - coin which is being borrowed, typically stablecoin
# * y - collateral coin (for example, wETH)
# * A - amplification, the measure of how concentrated the tick is
# * rate - interest rate
# * rate_mul - rate multiplier, 1 + integral(rate * dt)
# * active_band - current band. Other bands are either in one or other coin, but not both
# * min_band - bands below this are definitely empty
# * max_band - bands above this are definitely empty
# * bands_x[n], bands_y[n] - amounts of coin x or y deposited in band n
# * user_shares[user,n] / total_shares[n] - fraction of n'th band owned by a user
# * p_oracle - external oracle price (can be from another AMM)
# * p (as in get_p) - current price of AMM. It depends not only on the balances (x,y) in the band and active_band, but
# also on p_oracle
# * p_current_up, p_current_down - the value of p at constant p_oracle when y=0 or x=0 respectively for the band n
# * p_oracle_up, p_oracle_down - edges of the band when p=p_oracle (steady state), happen when x=0 or y=0 respectively,
# for band n.
# * Grid of bands is set for p_oracle values such as:
# * p_oracle_up(n) = base_price * ((A - 1) / A)**n
# * p_oracle_down(n) = p_oracle_up(n) * (A - 1) / A = p_oracle_up(n+1)
# * p_current_up and p_oracle_up change in opposite directions with n
# * When intereste is accrued - all the grid moves by change of base_price
#
# Bonding curve reads as:
# (f + x) * (g + y) = Inv = p_oracle * A**2 * y0**2
# =======================
interface ERC20:
def transfer(_to: address, _value: uint256) -> bool: nonpayable
def transferFrom(_from: address, _to: address, _value: uint256) -> bool: nonpayable
def approve(_spender: address, _value: uint256) -> bool: nonpayable
interface PriceOracle:
def price() -> uint256: view
def price_w() -> uint256: nonpayable
interface LMGauge:
def callback_collateral_shares(n: int256, collateral_per_share: DynArray[uint256, MAX_TICKS_UINT]): nonpayable
def callback_user_shares(user: address, n: int256, user_shares: DynArray[uint256, MAX_TICKS_UINT]): nonpayable
event TokenExchange:
buyer: indexed(address)
sold_id: uint256
tokens_sold: uint256
bought_id: uint256
tokens_bought: uint256
event Deposit:
provider: indexed(address)
amount: uint256
n1: int256
n2: int256
event Withdraw:
provider: indexed(address)
amount_borrowed: uint256
amount_collateral: uint256
event SetRate:
rate: uint256
rate_mul: uint256
time: uint256
event SetFee:
fee: uint256
event SetAdminFee:
fee: uint256
MAX_TICKS: constant(int256) = 50
MAX_TICKS_UINT: constant(uint256) = 50
MAX_SKIP_TICKS: constant(int256) = 1024
struct UserTicks:
ns: int256 # packs n1 and n2, each is int128
ticks: uint256[MAX_TICKS/2] # Share fractions packed 2 per slot
struct DetailedTrade:
in_amount: uint256
out_amount: uint256
n1: int256
n2: int256
ticks_in: DynArray[uint256, MAX_TICKS_UINT]
last_tick_j: uint256
admin_fee: uint256
BORROWED_TOKEN: immutable(ERC20) # x
BORROWED_PRECISION: immutable(uint256)
COLLATERAL_TOKEN: immutable(ERC20) # y
COLLATERAL_PRECISION: immutable(uint256)
BASE_PRICE: immutable(uint256)
admin: public(address)
A: public(immutable(uint256))
Aminus1: immutable(uint256)
A2: immutable(uint256)
Aminus12: immutable(uint256)
SQRT_BAND_RATIO: immutable(uint256) # sqrt(A / (A - 1))
LOG_A_RATIO: immutable(int256) # ln(A / (A - 1))
MAX_ORACLE_DN_POW: immutable(uint256) # (A / (A - 1)) ** 50
fee: public(uint256)
admin_fee: public(uint256)
rate: public(uint256)
rate_time: uint256
rate_mul: uint256
active_band: public(int256)
min_band: public(int256)
max_band: public(int256)
admin_fees_x: public(uint256)
admin_fees_y: public(uint256)
price_oracle_contract: public(PriceOracle)
old_p_o: uint256
old_dfee: uint256
prev_p_o_time: uint256
PREV_P_O_DELAY: constant(uint256) = 2 * 60 # s = 2 min
MAX_P_O_CHG: constant(uint256) = 12500 * 10**14 # <= 2**(1/3) - max relative change to have fee < 50%
bands_x: public(HashMap[int256, uint256])
bands_y: public(HashMap[int256, uint256])
total_shares: HashMap[int256, uint256]
user_shares: HashMap[address, UserTicks]
DEAD_SHARES: constant(uint256) = 1000
liquidity_mining_callback: public(LMGauge)
@external
def __init__(
_borrowed_token: address,
_borrowed_precision: uint256,
_collateral_token: address,
_collateral_precision: uint256,
_A: uint256,
_sqrt_band_ratio: uint256,
_log_A_ratio: int256,
_base_price: uint256,
fee: uint256,
admin_fee: uint256,
_price_oracle_contract: address,
):
"""
@notice LLAMMA constructor
@param _borrowed_token Token which is being borrowed
@param _collateral_token Token used as collateral
@param _collateral_precision Precision of collateral: we pass it because we want the blueprint to fit into bytecode
@param _A "Amplification coefficient" which also defines density of liquidity and band size. Relative band size is 1/_A
@param _sqrt_band_ratio Precomputed int(sqrt(A / (A - 1)) * 1e18)
@param _log_A_ratio Precomputed int(ln(A / (A - 1)) * 1e18)
@param _base_price Typically the initial crypto price at which AMM is deployed. Will correspond to band 0
@param fee Relative fee of the AMM: int(fee * 1e18)
@param admin_fee Admin fee: how much of fee goes to admin. 50% === int(0.5 * 1e18)
@param _price_oracle_contract External price oracle which has price() and price_w() methods
which both return current price of collateral multiplied by 1e18
"""
BORROWED_TOKEN = ERC20(_borrowed_token)
BORROWED_PRECISION = _borrowed_precision
COLLATERAL_TOKEN = ERC20(_collateral_token)
COLLATERAL_PRECISION = _collateral_precision
A = _A
BASE_PRICE = _base_price
Aminus1 = unsafe_sub(A, 1)
A2 = pow_mod256(A, 2)
Aminus12 = pow_mod256(unsafe_sub(A, 1), 2)
self.fee = fee
self.admin_fee = admin_fee
self.price_oracle_contract = PriceOracle(_price_oracle_contract)
self.prev_p_o_time = block.timestamp
self.old_p_o = self.price_oracle_contract.price()
self.rate_mul = 10**18
# sqrt(A / (A - 1)) - needs to be pre-calculated externally
SQRT_BAND_RATIO = _sqrt_band_ratio
# log(A / (A - 1)) - needs to be pre-calculated externally
LOG_A_RATIO = _log_A_ratio
# (A / (A - 1)) ** 50
MAX_ORACLE_DN_POW = unsafe_div(pow_mod256(unsafe_div(A**25 * 10**18, pow_mod256(Aminus1, 25)), 2), 10**18)
@internal
def approve_max(token: ERC20, _admin: address):
"""
Approve max in a separate function because it uses less bytespace than
calling directly, and gas doesn't matter in set_admin
"""
assert token.approve(_admin, max_value(uint256), default_return_value=True)
@external
def set_admin(_admin: address):
"""
@notice Set admin of the AMM. Typically it's a controller (unless it's tests)
@param _admin Admin address
"""
assert self.admin == empty(address)
self.admin = _admin
self.approve_max(BORROWED_TOKEN, _admin)
self.approve_max(COLLATERAL_TOKEN, _admin)
@internal
@pure
def sqrt_int(_x: uint256) -> uint256:
"""
@notice Wrapping isqrt builtin because otherwise it will be repeated every time instead of calling
@param _x Square root's input in "normal" units, e.g. sqrt_int(1) == 1
"""
return isqrt(_x)
@external
@pure
def coins(i: uint256) -> address:
return [BORROWED_TOKEN.address, COLLATERAL_TOKEN.address][i]
@internal
@view
def limit_p_o(p: uint256) -> uint256[2]:
"""
@notice Limits oracle price to avoid losses at abrupt changes, as well as calculates a dynamic fee.
If we consider oracle_change such as:
ratio = p_new / p_old
(let's take for simplicity p_new < p_old, otherwise we compute p_old / p_new)
Then if the minimal AMM fee will be:
fee = (1 - ratio**3),
AMM will not have a loss associated with the price change.
However, over time fee should still go down (over PREV_P_O_DELAY), and also ratio should be limited
because we don't want the fee to become too large (say, 50%) which is achieved by limiting the instantaneous
change in oracle price.
@return (limited_price_oracle, dynamic_fee)
"""
p_new: uint256 = p
dt: uint256 = unsafe_sub(PREV_P_O_DELAY, min(PREV_P_O_DELAY, block.timestamp - self.prev_p_o_time))
ratio: uint256 = 0
# ratio = 1 - (p_o_min / p_o_max)**3
if dt > 0:
old_p_o: uint256 = self.old_p_o
old_ratio: uint256 = self.old_dfee
# ratio = p_o_min / p_o_max
if p > old_p_o:
ratio = unsafe_div(old_p_o * 10**18, p)
if ratio < 10**36 / MAX_P_O_CHG:
p_new = unsafe_div(old_p_o * MAX_P_O_CHG, 10**18)
ratio = 10**36 / MAX_P_O_CHG
else:
ratio = unsafe_div(p * 10**18, old_p_o)
if ratio < 10**36 / MAX_P_O_CHG:
p_new = unsafe_div(old_p_o * 10**18, MAX_P_O_CHG)
ratio = 10**36 / MAX_P_O_CHG
# ratio is guaranteed to be less than 1e18
# Also guaranteed to be limited, therefore can have all ops unsafe
ratio = unsafe_div(
unsafe_mul(
unsafe_sub(unsafe_add(10**18, old_ratio), unsafe_div(pow_mod256(ratio, 3), 10**36)), # (f' + (1 - r**3))
dt), # * dt / T
PREV_P_O_DELAY)
return [p_new, ratio]
@internal
@view
def _price_oracle_ro() -> uint256[2]:
return self.limit_p_o(self.price_oracle_contract.price())
@internal
def _price_oracle_w() -> uint256[2]:
p: uint256[2] = self.limit_p_o(self.price_oracle_contract.price_w())
self.prev_p_o_time = block.timestamp
self.old_p_o = p[0]
self.old_dfee = p[1]
return p
@external
@view
def price_oracle() -> uint256:
"""
@notice Value returned by the external price oracle contract
"""
return self._price_oracle_ro()[0]
@external
@view
def dynamic_fee() -> uint256:
"""
@notice Dynamic fee which accounts for price_oracle shifts
"""
return max(self.fee, self._price_oracle_ro()[1])
@internal
@view
def _rate_mul() -> uint256:
"""
@notice Rate multiplier which is 1.0 + integral(rate, dt)
@return Rate multiplier in units where 1.0 == 1e18
"""
return unsafe_div(self.rate_mul * (10**18 + self.rate * (block.timestamp - self.rate_time)), 10**18)
@external
@view
def get_rate_mul() -> uint256:
"""
@notice Rate multiplier which is 1.0 + integral(rate, dt)
@return Rate multiplier in units where 1.0 == 1e18
"""
return self._rate_mul()
@internal
@view
def _base_price() -> uint256:
"""
@notice Price which corresponds to band 0.
Base price grows with time to account for interest rate (which is 0 by default)
"""
return unsafe_div(BASE_PRICE * self._rate_mul(), 10**18)
@external
@view
def get_base_price() -> uint256:
"""
@notice Price which corresponds to band 0.
Base price grows with time to account for interest rate (which is 0 by default)
"""
return self._base_price()
@internal
@view
def _p_oracle_up(n: int256) -> uint256:
"""
@notice Upper oracle price for the band to have liquidity when p = p_oracle
@param n Band number (can be negative)
@return Price at 1e18 base
"""
# p_oracle_up(n) = p_base * ((A - 1) / A) ** n
# p_oracle_down(n) = p_base * ((A - 1) / A) ** (n + 1) = p_oracle_up(n+1)
# return unsafe_div(self._base_price() * self.exp_int(-n * LOG_A_RATIO), 10**18)
power: int256 = -n * LOG_A_RATIO
# ((A - 1) / A) ** n = exp(-n * A / (A - 1)) = exp(-n * LOG_A_RATIO)
## Exp implementation based on solmate's
assert power > -42139678854452767551
assert power < 135305999368893231589
x: int256 = unsafe_div(unsafe_mul(power, 2**96), 10**18)
k: int256 = unsafe_div(
unsafe_add(
unsafe_div(unsafe_mul(x, 2**96), 54916777467707473351141471128),
2**95),
2**96)
x = unsafe_sub(x, unsafe_mul(k, 54916777467707473351141471128))
y: int256 = unsafe_add(x, 1346386616545796478920950773328)
y = unsafe_add(unsafe_div(unsafe_mul(y, x), 2**96), 57155421227552351082224309758442)
p: int256 = unsafe_sub(unsafe_add(y, x), 94201549194550492254356042504812)
p = unsafe_add(unsafe_div(unsafe_mul(p, y), 2**96), 28719021644029726153956944680412240)
p = unsafe_add(unsafe_mul(p, x), (4385272521454847904659076985693276 * 2**96))
q: int256 = x - 2855989394907223263936484059900
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 50020603652535783019961831881945)
q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 533845033583426703283633433725380)
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 3604857256930695427073651918091429)
q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 14423608567350463180887372962807573)
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 26449188498355588339934803723976023)
exp_result: uint256 = shift(
unsafe_mul(convert(unsafe_div(p, q), uint256), 3822833074963236453042738258902158003155416615667),
unsafe_sub(k, 195))
## End exp
return unsafe_div(self._base_price() * exp_result, 10**18)
@internal
@view
def _p_current_band(n: int256) -> uint256:
"""
@notice Lowest possible price of the band at current oracle price
@param n Band number (can be negative)
@return Price at 1e18 base
"""
# k = (self.A - 1) / self.A # equal to (p_down / p_up)
# p_base = self.p_base * k ** n = p_oracle_up(n)
p_base: uint256 = self._p_oracle_up(n)
# return self.p_oracle**3 / p_base**2
p_oracle: uint256 = self._price_oracle_ro()[0]
return unsafe_div(p_oracle**2 / p_base * p_oracle, p_base)
@external
@view
def p_current_up(n: int256) -> uint256:
"""
@notice Highest possible price of the band at current oracle price
@param n Band number (can be negative)
@return Price at 1e18 base
"""
return self._p_current_band(n + 1)
@external
@view
def p_current_down(n: int256) -> uint256:
"""
@notice Lowest possible price of the band at current oracle price
@param n Band number (can be negative)
@return Price at 1e18 base
"""
return self._p_current_band(n)
@external
@view
def p_oracle_up(n: int256) -> uint256:
"""
@notice Highest oracle price for the band to have liquidity when p = p_oracle
@param n Band number (can be negative)
@return Price at 1e18 base
"""
return self._p_oracle_up(n)
@external
@view
def p_oracle_down(n: int256) -> uint256:
"""
@notice Lowest oracle price for the band to have liquidity when p = p_oracle
@param n Band number (can be negative)
@return Price at 1e18 base
"""
return self._p_oracle_up(n + 1)
@internal
@view
def _get_y0(x: uint256, y: uint256, p_o: uint256, p_o_up: uint256) -> uint256:
"""
@notice Calculate y0 for the invariant based on current liquidity in band.
The value of y0 has a meaning of amount of collateral when band has no stablecoin
but current price is equal to both oracle price and upper band price.
@param x Amount of stablecoin in band
@param y Amount of collateral in band
@param p_o External oracle price
@param p_o_up Upper boundary of the band
@return y0
"""
assert p_o != 0
# solve:
# p_o * A * y0**2 - y0 * (p_oracle_up/p_o * (A-1) * x + p_o**2/p_oracle_up * A * y) - xy = 0
b: uint256 = 0
# p_o_up * unsafe_sub(A, 1) * x / p_o + A * p_o**2 / p_o_up * y / 10**18
if x != 0:
b = unsafe_div(p_o_up * Aminus1 * x, p_o)
if y != 0:
b += unsafe_div(A * p_o**2 / p_o_up * y, 10**18)
if x > 0 and y > 0:
D: uint256 = b**2 + unsafe_div(((4 * A) * p_o) * y, 10**18) * x
return unsafe_div((b + self.sqrt_int(D)) * 10**18, unsafe_mul(2 * A, p_o))
else:
return unsafe_div(b * 10**18, unsafe_mul(A, p_o))
@internal
@view
def _get_p(n: int256, x: uint256, y: uint256) -> uint256:
"""
@notice Get current AMM price in band
@param n Band number
@param x Amount of stablecoin in band
@param y Amount of collateral in band
@return Current price at 1e18 base
"""
p_o_up: uint256 = self._p_oracle_up(n)
p_o: uint256 = self._price_oracle_ro()[0]
assert p_o_up != 0
# Special cases
if x == 0:
if y == 0: # x and y are 0
# Return mid-band
return unsafe_div((unsafe_div(unsafe_div(p_o**2, p_o_up) * p_o, p_o_up) * A), Aminus1)
# if x == 0: # Lowest point of this band -> p_current_down
return unsafe_div(unsafe_div(p_o**2, p_o_up) * p_o, p_o_up)
if y == 0: # Highest point of this band -> p_current_up
p_o_up = unsafe_div(p_o_up * Aminus1, A) # now this is _actually_ p_o_down
return unsafe_div(p_o**2 / p_o_up * p_o, p_o_up)
y0: uint256 = self._get_y0(x, y, p_o, p_o_up)
# ^ that call also checks that p_o != 0
# (f(y0) + x) / (g(y0) + y)
f: uint256 = unsafe_div(A * y0 * p_o, p_o_up) * p_o
g: uint256 = unsafe_div(Aminus1 * y0 * p_o_up, p_o)
return (f + x * 10**18) / (g + y)
@external
@view
@nonreentrant('lock')
def get_p() -> uint256:
"""
@notice Get current AMM price in active_band
@return Current price at 1e18 base
"""
n: int256 = self.active_band
return self._get_p(n, self.bands_x[n], self.bands_y[n])
@internal
@view
def _read_user_tick_numbers(user: address) -> int256[2]:
"""
@notice Unpacks and reads user tick numbers
@param user User address
@return Lowest and highest band the user deposited into
"""
ns: int256 = self.user_shares[user].ns
n2: int256 = unsafe_div(ns, 2**128)
n1: int256 = ns % 2**128
if n1 >= 2**127:
n1 = unsafe_sub(n1, 2**128)
n2 = unsafe_add(n2, 1)
return [n1, n2]
@external
@view
@nonreentrant('lock')
def read_user_tick_numbers(user: address) -> int256[2]:
"""
@notice Unpacks and reads user tick numbers
@param user User address
@return Lowest and highest band the user deposited into
"""
return self._read_user_tick_numbers(user)
@internal
@view
def _read_user_ticks(user: address, ns: int256[2]) -> DynArray[uint256, MAX_TICKS_UINT]:
"""
@notice Unpacks and reads user ticks (shares) for all the ticks user deposited into
@param user User address
@param size Number of ticks the user deposited into
@return Array of shares the user has
"""
ticks: DynArray[uint256, MAX_TICKS_UINT] = []
size: uint256 = convert(ns[1] - ns[0] + 1, uint256)
for i in range(MAX_TICKS / 2):
if len(ticks) == size:
break
tick: uint256 = self.user_shares[user].ticks[i]
ticks.append(tick & (2**128 - 1))
if len(ticks) == size:
break
ticks.append(shift(tick, -128))
return ticks
@external
@view
@nonreentrant('lock')
def can_skip_bands(n_end: int256) -> bool:
"""
@notice Check that we have no liquidity between active_band and `n_end`
"""
n: int256 = self.active_band
for i in range(MAX_SKIP_TICKS):
if n_end > n:
if self.bands_y[n] != 0:
return False
n = unsafe_add(n, 1)
else:
if self.bands_x[n] != 0:
return False
n = unsafe_sub(n, 1)
if n == n_end: # not including n_end
break
return True
# Actually skipping bands:
# * change self.active_band to the new n
# * change self.p_base_mul
# to do n2-n1 times (if n2 > n1):
# out.base_mul = unsafe_div(out.base_mul * Aminus1, A)
@external
@view
@nonreentrant('lock')
def active_band_with_skip() -> int256:
n0: int256 = self.active_band
n: int256 = n0
min_band: int256 = self.min_band
for i in range(MAX_SKIP_TICKS):
if n < min_band:
n = n0 - MAX_SKIP_TICKS
break
if self.bands_x[n] != 0:
break
n -= 1
return n
@external
@view
@nonreentrant('lock')
def has_liquidity(user: address) -> bool:
"""
@notice Check if `user` has any liquidity in the AMM
"""
return self.user_shares[user].ticks[0] != 0
@internal
def save_user_shares(user: address, user_shares: DynArray[uint256, MAX_TICKS_UINT]):
ptr: uint256 = 0
for j in range(MAX_TICKS_UINT / 2):
if ptr >= len(user_shares):
break
tick: uint256 = user_shares[ptr]
ptr = unsafe_add(ptr, 1)
if len(user_shares) != ptr:
tick = tick | shift(user_shares[ptr], 128)
ptr = unsafe_add(ptr, 1)
self.user_shares[user].ticks[j] = tick
@external
@nonreentrant('lock')
def deposit_range(user: address, amount: uint256, n1: int256, n2: int256):
"""
@notice Deposit for a user in a range of bands. Only admin contract (Controller) can do it
@param user User address
@param amount Amount of collateral to deposit
@param n1 Lower band in the deposit range
@param n2 Upper band in the deposit range
"""
assert msg.sender == self.admin
user_shares: DynArray[uint256, MAX_TICKS_UINT] = []
collateral_shares: DynArray[uint256, MAX_TICKS_UINT] = []
n0: int256 = self.active_band
# We assume that n1,n2 area already sorted (and they are in Controller)
assert n2 < 2**127
assert n1 > -2**127
lm: LMGauge = self.liquidity_mining_callback
# Autoskip bands if we can
for i in range(MAX_SKIP_TICKS + 1):
if n1 > n0:
if i != 0:
self.active_band = n0
break
assert self.bands_x[n0] == 0 and i < MAX_SKIP_TICKS, "Deposit below current band"
n0 -= 1
n_bands: uint256 = unsafe_add(convert(unsafe_sub(n2, n1), uint256), 1)
assert n_bands <= MAX_TICKS_UINT
y_per_band: uint256 = unsafe_div(amount * COLLATERAL_PRECISION, n_bands)
assert y_per_band > 100, "Amount too low"
assert self.user_shares[user].ticks[0] == 0 # dev: User must have no liquidity
self.user_shares[user].ns = unsafe_add(n1, unsafe_mul(n2, 2**128))
for i in range(MAX_TICKS):
band: int256 = unsafe_add(n1, i)
if band > n2:
break
assert self.bands_x[band] == 0, "Band not empty"
y: uint256 = y_per_band
if i == 0:
y = amount * COLLATERAL_PRECISION - y * unsafe_sub(n_bands, 1)
total_y: uint256 = self.bands_y[band]
# Total / user share
s: uint256 = self.total_shares[band]
ds: uint256 = unsafe_div((s + DEAD_SHARES) * y, total_y + 1)
assert ds > 0, "Amount too low"
user_shares.append(ds)
s += ds
assert s <= 2**128 - 1
self.total_shares[band] = s
total_y += y
self.bands_y[band] = total_y
if lm.address != empty(address):
# If initial s == 0 - s becomes equal to y which is > 100 => nonzero
collateral_shares.append(unsafe_div(total_y * 10**18, s))
self.min_band = min(self.min_band, n1)
self.max_band = max(self.max_band, n2)
self.save_user_shares(user, user_shares)
self.rate_mul = self._rate_mul()
self.rate_time = block.timestamp
log Deposit(user, amount, n1, n2)
if lm.address != empty(address):
lm.callback_collateral_shares(n1, collateral_shares)
lm.callback_user_shares(user, n1, user_shares)
@external
@nonreentrant('lock')
def withdraw(user: address, frac: uint256) -> uint256[2]:
"""
@notice Withdraw all liquidity for the user. Only admin contract can do it
@param user User who owns liquidity
@param frac Fraction to withdraw (1e18 being 100%)
@return Amount of [stablecoins, collateral] withdrawn
"""
assert msg.sender == self.admin
assert frac <= 10**18
lm: LMGauge = self.liquidity_mining_callback
ns: int256[2] = self._read_user_tick_numbers(user)
n: int256 = ns[0]
user_shares: DynArray[uint256, MAX_TICKS_UINT] = self._read_user_ticks(user, ns)
assert user_shares[0] > 0, "No deposits"
total_x: uint256 = 0
total_y: uint256 = 0
min_band: int256 = self.min_band
old_min_band: int256 = min_band
max_band: int256 = self.max_band
old_max_band: int256 = max_band
for i in range(MAX_TICKS):
x: uint256 = self.bands_x[n]
y: uint256 = self.bands_y[n]
ds: uint256 = unsafe_div(frac * user_shares[i], 10**18) # Can ONLY zero out when frac == 10**18
user_shares[i] = unsafe_sub(user_shares[i], ds)
s: uint256 = self.total_shares[n]
new_shares: uint256 = s - ds
self.total_shares[n] = new_shares
s += DEAD_SHARES
dx: uint256 = (x + 1) * ds / s
dy: uint256 = unsafe_div((y + 1) * ds, s)
x -= dx
y -= dy
# If withdrawal is the last one - tranfer dust to admin fees
if new_shares == 0:
if x > 0:
self.admin_fees_x += x
if y > 0:
self.admin_fees_y += y / COLLATERAL_PRECISION
x = 0
y = 0
if n == min_band:
if x == 0:
if y == 0:
min_band += 1
if x > 0 or y > 0:
max_band = n
self.bands_x[n] = x
self.bands_y[n] = y
total_x += dx
total_y += dy
if n == ns[1]:
break
else:
n = unsafe_add(n, 1)
# Empty the ticks
if frac == 10**18:
self.user_shares[user].ticks[0] = 0
else:
self.save_user_shares(user, user_shares)
if old_min_band != min_band:
self.min_band = min_band
if old_max_band <= ns[1]:
self.max_band = max_band
total_x = unsafe_div(total_x, BORROWED_PRECISION)
total_y = unsafe_div(total_y, COLLATERAL_PRECISION)
log Withdraw(user, total_x, total_y)
self.rate_mul = self._rate_mul()
self.rate_time = block.timestamp
if lm.address != empty(address):
lm.callback_collateral_shares(0, []) # collateral/shares ratio is unchanged
lm.callback_user_shares(user, ns[0], user_shares)
return [total_x, total_y]
@internal
@view
def calc_swap_out(pump: bool, in_amount: uint256, p_o: uint256[2], in_precision: uint256, out_precision: uint256) -> DetailedTrade:
"""
@notice Calculate the amount which can be obtained as a result of exchange.
If couldn't exchange all - will also update the amount which was actually used.
Also returns other parameters related to state after swap.
This function is core to the AMM functionality.
@param pump Indicates whether the trade buys or sells collateral
@param in_amount Amount of token going in
@param p_o Current oracle price and ratio (p_o, dynamic_fee)
@return Amounts spent and given out, initial and final bands of the AMM, new
amounts of coins in bands in the AMM, as well as admin fee charged,
all in one data structure
"""
# pump = True: borrowable (USD) in, collateral (ETH) out; going up
# pump = False: collateral (ETH) in, borrowable (USD) out; going down
min_band: int256 = self.min_band
max_band: int256 = self.max_band
out: DetailedTrade = empty(DetailedTrade)
out.n2 = self.active_band
p_o_up: uint256 = self._p_oracle_up(out.n2)
x: uint256 = self.bands_x[out.n2]
y: uint256 = self.bands_y[out.n2]
in_amount_left: uint256 = in_amount
antifee: uint256 = unsafe_div(
(10**18)**2,
unsafe_sub(10**18, max(self.fee, p_o[1]))
)
admin_fee: uint256 = self.admin_fee
j: uint256 = MAX_TICKS_UINT
for i in range(MAX_TICKS + MAX_SKIP_TICKS):
y0: uint256 = 0
f: uint256 = 0
g: uint256 = 0
Inv: uint256 = 0
if x > 0 or y > 0:
if j == MAX_TICKS_UINT:
out.n1 = out.n2
j = 0
y0 = self._get_y0(x, y, p_o[0], p_o_up) # <- also checks p_o
f = unsafe_div(A * y0 * p_o[0] / p_o_up * p_o[0], 10**18)
g = unsafe_div(Aminus1 * y0 * p_o_up, p_o[0])
Inv = (f + x) * (g + y)
if j != MAX_TICKS_UINT:
# Initialize
_tick: uint256 = y
if pump:
_tick = x
out.ticks_in.append(_tick)
# Need this to break if price is too far
p_ratio: uint256 = unsafe_div(p_o_up * 10**18, p_o[0])
if pump:
if y != 0:
if g != 0:
x_dest: uint256 = (unsafe_div(Inv, g) - f) - x
dx: uint256 = unsafe_div(x_dest * antifee, 10**18)
if dx >= in_amount_left:
# This is the last band
x_dest = unsafe_div(in_amount_left * 10**18, antifee) # LESS than in_amount_left
out.last_tick_j = min(Inv / (f + (x + x_dest)) - g + 1, y) # Should be always >= 0
x_dest = unsafe_div(unsafe_sub(in_amount_left, x_dest) * admin_fee, 10**18) # abs admin fee now
x += in_amount_left # x is precise after this
# Round down the output
out.out_amount += y - out.last_tick_j
out.ticks_in[j] = x - x_dest
out.in_amount = in_amount
out.admin_fee = unsafe_add(out.admin_fee, x_dest)
break
else:
# We go into the next band
dx = max(dx, 1) # Prevents from leaving dust in the band
x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18) # abs admin fee now
in_amount_left -= dx
out.ticks_in[j] = x + dx - x_dest
out.in_amount += dx
out.out_amount += y
out.admin_fee = unsafe_add(out.admin_fee, x_dest)
if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
if out.n2 == max_band:
break
if j == MAX_TICKS_UINT - 1:
break
if p_ratio < 10**36 / MAX_ORACLE_DN_POW:
# Don't allow to be away by more than ~50 ticks
break
out.n2 += 1
p_o_up = unsafe_div(p_o_up * Aminus1, A)
x = 0
y = self.bands_y[out.n2]
else: # dump
if x != 0:
if f != 0:
y_dest: uint256 = (unsafe_div(Inv, f) - g) - y
dy: uint256 = unsafe_div(y_dest * antifee, 10**18)
if dy >= in_amount_left:
# This is the last band
y_dest = unsafe_div(in_amount_left * 10**18, antifee)
out.last_tick_j = min(Inv / (g + (y + y_dest)) - f + 1, x)
y_dest = unsafe_div(unsafe_sub(in_amount_left, y_dest) * admin_fee, 10**18) # abs admin fee now
y += in_amount_left
out.out_amount += x - out.last_tick_j
out.ticks_in[j] = y - y_dest
out.in_amount = in_amount
out.admin_fee = unsafe_add(out.admin_fee, y_dest)
break
else:
# We go into the next band
dy = max(dy, 1) # Prevents from leaving dust in the band
y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18) # abs admin fee now
in_amount_left -= dy
out.ticks_in[j] = y + dy - y_dest
out.in_amount += dy
out.out_amount += x
out.admin_fee = unsafe_add(out.admin_fee, y_dest)
if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
if out.n2 == min_band:
break
if j == MAX_TICKS_UINT - 1:
break
if p_ratio > MAX_ORACLE_DN_POW:
# Don't allow to be away by more than ~50 ticks
break
out.n2 -= 1
p_o_up = unsafe_div(p_o_up * A, Aminus1)
x = self.bands_x[out.n2]
y = 0
if j != MAX_TICKS_UINT:
j = unsafe_add(j, 1)
# Round up what goes in and down what goes out
# ceil(in_amount_used/BORROWED_PRECISION) * BORROWED_PRECISION
out.in_amount = unsafe_mul(unsafe_div(unsafe_add(out.in_amount, unsafe_sub(in_precision, 1)), in_precision), in_precision)
out.out_amount = unsafe_mul(unsafe_div(out.out_amount, out_precision), out_precision)
return out
@internal
@view
def _get_dxdy(i: uint256, j: uint256, amount: uint256, is_in: bool) -> DetailedTrade:
"""
@notice Method to use to calculate out amount and spent in amount
@param i Input coin index
@param j Output coin index
@param amount Amount of input coin to swap
@param is_in Whether IN our OUT amount is known
@return DetailedTrade with all swap results
"""
# i = 0: borrowable (USD) in, collateral (ETH) out; going up
# i = 1: collateral (ETH) in, borrowable (USD) out; going down
assert (i == 0 and j == 1) or (i == 1 and j == 0), "Wrong index"
out: DetailedTrade = empty(DetailedTrade)
if amount == 0:
return out
in_precision: uint256 = COLLATERAL_PRECISION
out_precision: uint256 = BORROWED_PRECISION
if i == 0:
in_precision = BORROWED_PRECISION
out_precision = COLLATERAL_PRECISION
p_o: uint256[2] = self._price_oracle_ro()
if is_in:
out = self.calc_swap_out(i == 0, amount * in_precision, p_o, in_precision, out_precision)
else:
out = self.calc_swap_in(i == 0, amount * out_precision, p_o, in_precision, out_precision)
out.in_amount = unsafe_div(out.in_amount, in_precision)
out.out_amount = unsafe_div(out.out_amount, out_precision)
return out
@external
@view
@nonreentrant('lock')
def get_dy(i: uint256, j: uint256, in_amount: uint256) -> uint256:
"""
@notice Method to use to calculate out amount
@param i Input coin index
@param j Output coin index
@param in_amount Amount of input coin to swap
@return Amount of coin j to give out
"""
return self._get_dxdy(i, j, in_amount, True).out_amount
@external
@view
@nonreentrant('lock')
def get_dxdy(i: uint256, j: uint256, in_amount: uint256) -> (uint256, uint256):
"""
@notice Method to use to calculate out amount and spent in amount
@param i Input coin index
@param j Output coin index
@param in_amount Amount of input coin to swap
@return A tuple with in_amount used and out_amount returned
"""
out: DetailedTrade = self._get_dxdy(i, j, in_amount, True)
return (out.in_amount, out.out_amount)
@internal
def _exchange(i: uint256, j: uint256, amount: uint256, minmax_amount: uint256, _for: address, use_in_amount: bool) -> uint256[2]:
"""
@notice Exchanges two coins, callable by anyone
@param i Input coin index
@param j Output coin index
@param amount Amount of input/output coin to swap
@param minmax_amount Minimal/maximum amount to get as output/input
@param _for Address to send coins to
@param use_in_amount Whether input or output amount is specified
@return Amount of coins given in and out
"""
assert (i == 0 and j == 1) or (i == 1 and j == 0), "Wrong index"
p_o: uint256[2] = self._price_oracle_w() # Let's update the oracle even if we exchange 0
if amount == 0:
return [0, 0]
lm: LMGauge = self.liquidity_mining_callback
collateral_shares: DynArray[uint256, MAX_TICKS_UINT] = []
in_coin: ERC20 = BORROWED_TOKEN
out_coin: ERC20 = COLLATERAL_TOKEN
in_precision: uint256 = BORROWED_PRECISION
out_precision: uint256 = COLLATERAL_PRECISION
if i == 1:
in_precision = out_precision
in_coin = out_coin
out_precision = BORROWED_PRECISION
out_coin = BORROWED_TOKEN
out: DetailedTrade = empty(DetailedTrade)
if use_in_amount:
out = self.calc_swap_out(i == 0, amount * in_precision, p_o, in_precision, out_precision)
else:
out = self.calc_swap_in(i == 0, amount * out_precision, p_o, in_precision, out_precision)
in_amount_done: uint256 = unsafe_div(out.in_amount, in_precision)
out_amount_done: uint256 = unsafe_div(out.out_amount, out_precision)
if use_in_amount:
assert out_amount_done >= minmax_amount, "Slippage"
else:
assert in_amount_done <= minmax_amount, "Slippage"
if out_amount_done == 0 or in_amount_done == 0:
return [0, 0]
out.admin_fee = unsafe_div(out.admin_fee, in_precision)
if i == 0:
self.admin_fees_x += out.admin_fee
else:
self.admin_fees_y += out.admin_fee
assert in_coin.transferFrom(msg.sender, self, in_amount_done, default_return_value=True)
assert out_coin.transfer(_for, out_amount_done, default_return_value=True)
n: int256 = min(out.n1, out.n2)
n_start: int256 = n
n_diff: int256 = abs(unsafe_sub(out.n2, out.n1))
for k in range(MAX_TICKS):
x: uint256 = 0
y: uint256 = 0
if i == 0:
x = out.ticks_in[k]
if n == out.n2:
y = out.last_tick_j
else:
y = out.ticks_in[unsafe_sub(n_diff, k)]
if n == out.n2:
x = out.last_tick_j
self.bands_x[n] = x
self.bands_y[n] = y
if lm.address != empty(address):
s: uint256 = 0
if y > 0:
s = unsafe_div(y * 10**18, self.total_shares[n])
collateral_shares.append(s)
if k == n_diff:
break
n = unsafe_add(n, 1)
self.active_band = out.n2
log TokenExchange(_for, i, in_amount_done, j, out_amount_done)
if lm.address != empty(address):
lm.callback_collateral_shares(n_start, collateral_shares)
return [in_amount_done, out_amount_done]
@internal
@view
def calc_swap_in(pump: bool, out_amount: uint256, p_o: uint256[2], in_precision: uint256, out_precision: uint256) -> DetailedTrade:
"""
@notice Calculate the input amount required to receive the desired output amount.
If couldn't exchange all - will also update the amount which was actually received.
Also returns other parameters related to state after swap.
@param pump Indicates whether the trade buys or sells collateral
@param out_amount Desired amount of token going out
@param p_o Current oracle price and antisandwich fee (p_o, dynamic_fee)
@return Amounts required and given out, initial and final bands of the AMM, new
amounts of coins in bands in the AMM, as well as admin fee charged,
all in one data structure
"""
# pump = True: borrowable (USD) in, collateral (ETH) out; going up
# pump = False: collateral (ETH) in, borrowable (USD) out; going down
min_band: int256 = self.min_band
max_band: int256 = self.max_band
out: DetailedTrade = empty(DetailedTrade)
out.n2 = self.active_band
p_o_up: uint256 = self._p_oracle_up(out.n2)
x: uint256 = self.bands_x[out.n2]
y: uint256 = self.bands_y[out.n2]
out_amount_left: uint256 = out_amount
antifee: uint256 = unsafe_div(
(10**18)**2,
unsafe_sub(10**18, max(self.fee, p_o[1]))
)
admin_fee: uint256 = self.admin_fee
j: uint256 = MAX_TICKS_UINT
for i in range(MAX_TICKS + MAX_SKIP_TICKS):
y0: uint256 = 0
f: uint256 = 0
g: uint256 = 0
Inv: uint256 = 0
if x > 0 or y > 0:
if j == MAX_TICKS_UINT:
out.n1 = out.n2
j = 0
y0 = self._get_y0(x, y, p_o[0], p_o_up) # <- also checks p_o
f = unsafe_div(A * y0 * p_o[0] / p_o_up * p_o[0], 10**18)
g = unsafe_div(Aminus1 * y0 * p_o_up, p_o[0])
Inv = (f + x) * (g + y)
if j != MAX_TICKS_UINT:
# Initialize
_tick: uint256 = y
if pump:
_tick = x
out.ticks_in.append(_tick)
# Need this to break if price is too far
p_ratio: uint256 = unsafe_div(p_o_up * 10**18, p_o[0])
if pump:
if y != 0:
if g != 0:
if y >= out_amount_left:
# This is the last band
out.last_tick_j = unsafe_sub(y, out_amount_left)
x_dest: uint256 = Inv / (g + out.last_tick_j) - f - x
dx: uint256 = unsafe_div(x_dest * antifee, 10**18) # MORE than x_dest
out.out_amount = out_amount # We successfully found liquidity for all the out_amount
out.in_amount += dx
x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18) # abs admin fee now
out.ticks_in[j] = x + dx - x_dest
out.admin_fee = unsafe_add(out.admin_fee, x_dest)
break
else:
# We go into the next band
x_dest: uint256 = (unsafe_div(Inv, g) - f) - x
dx: uint256 = max(unsafe_div(x_dest * antifee, 10**18), 1)
out_amount_left -= y
out.in_amount += dx
out.out_amount += y
x_dest = unsafe_div(unsafe_sub(dx, x_dest) * admin_fee, 10**18) # abs admin fee now
out.ticks_in[j] = x + dx - x_dest
out.admin_fee = unsafe_add(out.admin_fee, x_dest)
if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
if out.n2 == max_band:
break
if j == MAX_TICKS_UINT - 1:
break
if p_ratio < 10**36 / MAX_ORACLE_DN_POW:
# Don't allow to be away by more than ~50 ticks
break
out.n2 += 1
p_o_up = unsafe_div(p_o_up * Aminus1, A)
x = 0
y = self.bands_y[out.n2]
else: # dump
if x != 0:
if f != 0:
if x >= out_amount_left:
# This is the last band
out.last_tick_j = unsafe_sub(x, out_amount_left)
y_dest: uint256 = Inv / (f + out.last_tick_j) - g - y
dy: uint256 = unsafe_div(y_dest * antifee, 10**18) # MORE than y_dest
out.out_amount = out_amount
out.in_amount += dy
y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18) # abs admin fee now
out.ticks_in[j] = y + dy - y_dest
out.admin_fee = unsafe_add(out.admin_fee, y_dest)
break
else:
# We go into the next band
y_dest: uint256 = (unsafe_div(Inv, f) - g) - y
dy: uint256 = max(unsafe_div(y_dest * antifee, 10**18), 1)
out_amount_left -= x
out.in_amount += dy
out.out_amount += x
y_dest = unsafe_div(unsafe_sub(dy, y_dest) * admin_fee, 10**18) # abs admin fee now
out.ticks_in[j] = y + dy - y_dest
out.admin_fee = unsafe_add(out.admin_fee, y_dest)
if i != MAX_TICKS + MAX_SKIP_TICKS - 1:
if out.n2 == min_band:
break
if j == MAX_TICKS_UINT - 1:
break
if p_ratio > MAX_ORACLE_DN_POW:
# Don't allow to be away by more than ~50 ticks
break
out.n2 -= 1
p_o_up = unsafe_div(p_o_up * A, Aminus1)
x = self.bands_x[out.n2]
y = 0
if j != MAX_TICKS_UINT:
j = unsafe_add(j, 1)
# Round up what goes in and down what goes out
# ceil(in_amount_used/BORROWED_PRECISION) * BORROWED_PRECISION
out.in_amount = unsafe_mul(unsafe_div(unsafe_add(out.in_amount, unsafe_sub(in_precision, 1)), in_precision), in_precision)
out.out_amount = unsafe_mul(unsafe_div(out.out_amount, out_precision), out_precision)
return out
@external
@view
@nonreentrant('lock')
def get_dx(i: uint256, j: uint256, out_amount: uint256) -> uint256:
"""
@notice Method to use to calculate in amount required to receive the desired out_amount
@param i Input coin index
@param j Output coin index
@param out_amount Desired amount of output coin to receive
@return Amount of coin i to spend
"""
# i = 0: borrowable (USD) in, collateral (ETH) out; going up
# i = 1: collateral (ETH) in, borrowable (USD) out; going down
return self._get_dxdy(i, j, out_amount, False).in_amount
@external
@view
@nonreentrant('lock')
def get_dydx(i: uint256, j: uint256, out_amount: uint256) -> (uint256, uint256):
"""
@notice Method to use to calculate in amount required and out amount received
@param i Input coin index
@param j Output coin index
@param out_amount Desired amount of output coin to receive
@return A tuple with out_amount received and in_amount returned
"""
# i = 0: borrowable (USD) in, collateral (ETH) out; going up
# i = 1: collateral (ETH) in, borrowable (USD) out; going down
out: DetailedTrade = self._get_dxdy(i, j, out_amount, False)
return (out.out_amount, out.in_amount)
@external
@nonreentrant('lock')
def exchange(i: uint256, j: uint256, in_amount: uint256, min_amount: uint256, _for: address = msg.sender) -> uint256[2]:
"""
@notice Exchanges two coins, callable by anyone
@param i Input coin index
@param j Output coin index
@param in_amount Amount of input coin to swap
@param min_amount Minimal amount to get as output
@param _for Address to send coins to
@return Amount of coins given in/out
"""
return self._exchange(i, j, in_amount, min_amount, _for, True)
@external
@nonreentrant('lock')
def exchange_dy(i: uint256, j: uint256, out_amount: uint256, max_amount: uint256, _for: address = msg.sender) -> uint256[2]:
"""
@notice Exchanges two coins, callable by anyone
@param i Input coin index
@param j Output coin index
@param out_amount Desired amount of output coin to receive
@param max_amount Maximum amount to spend (revert if more)
@param _for Address to send coins to
@return Amount of coins given in/out
"""
return self._exchange(i, j, out_amount, max_amount, _for, False)
@internal
@view
def get_xy_up(user: address, use_y: bool) -> uint256:
"""
@notice Measure the amount of y (collateral) in the band n if we adiabatically trade near p_oracle on the way up,
or the amount of x (stablecoin) if we trade adiabatically down
@param user User the amount is calculated for
@param use_y Calculate amount of collateral if True and of stablecoin if False
@return Amount of coins
"""
ns: int256[2] = self._read_user_tick_numbers(user)
ticks: DynArray[uint256, MAX_TICKS_UINT] = self._read_user_ticks(user, ns)
if ticks[0] == 0: # Even dynamic array will have 0th element set here
return 0
p_o: uint256 = self._price_oracle_ro()[0]
assert p_o != 0
n: int256 = ns[0] - 1
n_active: int256 = self.active_band
p_o_down: uint256 = self._p_oracle_up(ns[0])
XY: uint256 = 0
for i in range(MAX_TICKS):
n += 1
if n > ns[1]:
break
x: uint256 = 0
y: uint256 = 0
if n >= n_active:
y = self.bands_y[n]
if n <= n_active:
x = self.bands_x[n]
# p_o_up: uint256 = self._p_oracle_up(n)
p_o_up: uint256 = p_o_down
# p_o_down = self._p_oracle_up(n + 1)
p_o_down = unsafe_div(p_o_down * Aminus1, A)
if x == 0:
if y == 0:
continue
total_share: uint256 = self.total_shares[n]
user_share: uint256 = ticks[i]
if total_share == 0:
continue
if user_share == 0:
continue
total_share += DEAD_SHARES
# Also ideally we'd want to add +1 to all quantities when calculating with shares
# but we choose to save bytespace and slightly under-estimate the result of this call
# which is also more conservative
# Also this will revert if p_o_down is 0, and p_o_down is 0 if p_o_up is 0
p_current_mid: uint256 = unsafe_div(unsafe_div(p_o**2 / p_o_down * p_o, p_o_down) * Aminus1, A)
# if p_o > p_o_up - we "trade" everything to y and then convert to the result
# if p_o < p_o_down - "trade" to x, then convert to result
# otherwise we are in-band, so we do the more complex logic to trade
# to p_o rather than to the edge of the band
# trade to the edge of the band == getting to the band edge while p_o=const
# Cases when special conversion is not needed (to save on computations)
if x == 0 or y == 0:
if p_o > p_o_up: # p_o < p_current_down
# all to y at constant p_o, then to target currency adiabatically
...
// [truncated — 60528 bytes total]
Read Contract
A 0xf446c1d0 → uint256
active_band 0x8f8654c5 → int256
active_band_with_skip 0xc16ef264 → int256
admin 0xf851a440 → address
admin_fee 0xfee3f7f9 → uint256
admin_fees_x 0xd1fea733 → uint256
admin_fees_y 0x89960ba7 → uint256
bands_x 0xebcb0067 → uint256
bands_y 0x31f7e306 → uint256
can_skip_bands 0xec654706 → bool
coins 0xc6610657 → address
dynamic_fee 0x77c34594 → uint256
fee 0xddca3f43 → uint256
get_amount_for_price 0x48e995f9 → uint256, bool
get_base_price 0xa7db79a5 → uint256
get_dx 0x37ed3a7a → uint256
get_dxdy 0xc49202e7 → uint256, uint256
get_dy 0x556d6e9f → uint256
get_dydx 0xed7110cf → uint256, uint256
get_p 0xf2388acb → uint256
get_rate_mul 0x095a0fc6 → uint256
get_sum_xy 0x544fb5c1 → uint256[2]
get_x_down 0x62ca4b18 → uint256
get_xy 0x84738380 → uint256[][2]
get_y_up 0xee4c32ee → uint256
has_liquidity 0xe8dd1ef1 → bool
liquidity_mining_callback 0x611105d3 → address
max_band 0xaaa615fc → int256
min_band 0xca72a821 → int256
p_current_down 0xc32bd03c → uint256
p_current_up 0x7c1bbd83 → uint256
p_oracle_down 0x24299b7a → uint256
p_oracle_up 0x2eb858e7 → uint256
price_oracle 0x86fc88d3 → uint256
price_oracle_contract 0x5ea0e01b → address
rate 0x2c4e722e → uint256
read_user_tick_numbers 0xb461100d → int256[2]
Write Contract 12 functions
These functions modify contract state and require a wallet transaction to execute.
deposit_range 0xab047e00
address user
uint256 amount
int256 n1
int256 n2
exchange 0x5b41b908
uint256 i
uint256 j
uint256 in_amount
uint256 min_amount
returns: uint256[2]
exchange 0xa64833a0
uint256 i
uint256 j
uint256 in_amount
uint256 min_amount
address _for
returns: uint256[2]
exchange_dy 0xa3e346ec
uint256 i
uint256 j
uint256 out_amount
uint256 max_amount
returns: uint256[2]
exchange_dy 0x3c10269a
uint256 i
uint256 j
uint256 out_amount
uint256 max_amount
address _for
returns: uint256[2]
reset_admin_fees 0x822fe507
No parameters
set_admin 0xe9333fab
address _admin
set_admin_fee 0x3217902f
uint256 fee
set_callback 0xcc1891c7
address liquidity_mining_callback
set_fee 0x1aa02d59
uint256 fee
set_rate 0xd4387a99
uint256 rate
returns: uint256
withdraw 0xf3fef3a3
address user
uint256 frac
returns: uint256[2]
Token Balances (1)
View Transfers →Recent Transactions
No transactions found for this address