Address Contract Partially Verified
Address
0x618788357D0EBd8A37e763ADab3bc575D54c2C7d
Balance
0 ETH
Nonce
1
Code Size
24557 bytes
Creator
0x3E0139cE...1d51 at tx 0xd8ed515d...3afbe2
Indexed Transactions
0 (1 on-chain, 0.8% indexed)
Contract Bytecode
24557 bytes
0x600436101561000d576144fa565b600035601c52600051341561002157600080fd5b63f446c1d08114156100505760065801614500565b610140526101405160648082049050905060005260206000f35b6376a2f0f08114156100765760065801614500565b610140526101405160005260206000f35b63bb7b8b8081141561009c5760065801614fb3565b610140526101405160005260206000f35b635ae7a3fe8114156101495760065801614fb3565b6101405261014051670de0b6b3a764000080820282158284830414176100d657600080fd5b8090509050905061014051600658016147e1565b61016052610140526101605161018052610140516101605161018051610180516101a0526101a05160065801614698565b6102005261018052610160526101405261020051808061013a57600080fd5b82049050905060005260206000f35b63ed8e84f38114156104b65760443560011c1561016557600080fd5b6101405160065801614500565b61016052610140526101605161014052610140516101605161018051600658016147e1565b6101a0526101805261016052610140526101a0516101c0526101405161016051610180516101a0516101c05160065801614adf565b6101e0526101c0526101a0526101805261016052610140526101e051610200526101c05161016052610200516101805260018060c052602060c020546101a05260018160c052602060c02001546101c052506101405161016051610180516101a0516101c0516101e051610160516102005261018051610220526101a051610240526101c051610260526101405161028052610280516102605161024051610220516102005160065801614e56565b6102e0526101e0526101c0526101a0526101805261016052610140526102e0516101e05261020060006002818352015b60443515610303576101a061020051600281106102c757600080fd5b602002018051600461020051600281106102e057600080fd5b60200201358181830110156102f457600080fd5b8082019050905081525061034d565b6101a0610200516002811061031757600080fd5b6020020180516004610200516002811061033057600080fd5b60200201358082101561034257600080fd5b808203905090508152505b81516001018083528114156102ab575b50506101405161016051610180516101a0516101c0516101e05161020051610160516102205261018051610240526101a051610260526101c05161028052610140516102a0526102a0516102805161026051610240516102205160065801614e56565b61030052610200526101e0526101c0526101a052610180526101605261014052610300516102005260206102a060046318160ddd6102405261025c6005545afa61040957600080fd5b601f3d1161041657600080fd5b6000506102a051610220526000610240526044351561045457610200516101e0518082101561044457600080fd5b8082039050905061024052610475565b6101e051610200518082101561046957600080fd5b80820390509050610240525b6102405161022051808202821582848304141761049157600080fd5b809050905090506101e05180806104a757600080fd5b82049050905060005260206000f35b630b4c7e4d811415610e4557601654156104cf57600080fd5b6001601655601454156104e157600080fd5b6101405160065801614500565b61016052610140526101605161014052610140516101605161018051600658016147e1565b6101a0526101805261016052610140526101a0516101c0526101405161016051610180516101a0516101c05160065801614adf565b6101e0526101c0526101a0526101805261016052610140526101e051610200526101c05161016052610200516101805260018060c052602060c020546101a05260018160c052602060c02001546101c052506101405161016051610180516101a0516101c0516101e051610160516102005261018051610220526101a051610240526101c051610260526101405161028052610280516102605161024051610220516102005160065801614e56565b6102e0526101e0526101c0526101a0526101805261016052610140526102e0516101e0526005546102005260206102a060046318160ddd6102405261025c610200515afa61064457600080fd5b601f3d1161065157600080fd5b6000506102a051610220526101a051610240526101c0516102605261028060006002818352015b6102205115156106a65760006004610280516002811061069757600080fd5b6020020135116106a657600080fd5b6101a061028051600281106106ba57600080fd5b6020020151600461028051600281106106d257600080fd5b60200201358181830110156106e657600080fd5b80820190509050610240610280516002811061070157600080fd5b60200201525b8151600101808352811415610678575b50506101405161016051610180516101a0516101c0516101e0516102005161022051610240516102605161028051610160516102a052610180516102c052610240516102e0526102605161030052610140516103205261032051610300516102e0516102c0516102a05160065801614e56565b6103805261028052610260526102405261022052610200526101e0526101c0526101a05261018052610160526101405261038051610280526101e05161028051116107d457600080fd5b6040366102a037610280516102e0526000610300526000610220511115610b53576002546002808202821582848304141761080e57600080fd5b80905090509050600480820490509050610320526003546103405261036060006002818352015b610280516101a0610360516002811061084d57600080fd5b6020020151808202821582848304141761086657600080fd5b809050905090506101e051808061087c57600080fd5b8204905090506103805260006103a05261024061036051600281106108a057600080fd5b60200201516103805111156108e9576103805161024061036051600281106108c757600080fd5b6020020151808210156108d957600080fd5b808203905090506103a05261091f565b61024061036051600281106108fd57600080fd5b6020020151610380518082101561091357600080fd5b808203905090506103a0525b610320516103a051808202821582848304141761093b57600080fd5b809050905090506402540be400808204905090506102a0610360516002811061096357600080fd5b6020020152610240610360516002811061097c57600080fd5b60200201516102a0610360516002811061099557600080fd5b60200201516103405180820282158284830414176109b257600080fd5b809050905090506402540be40080820490509050808210156109d357600080fd5b8082039050905061036051600281106109eb57600080fd5b600160c052602060c02001556102406103605160028110610a0b57600080fd5b6020020180516102a06103605160028110610a2557600080fd5b602002015180821015610a3757600080fd5b808203905090508152505b8151600101808352811415610835575b5050610140610360525b61036051516020610360510161036052610360610360511015610a7e57610a5c565b6101605161038052610180516103a052610240516103c052610260516103e0526101405161040052610400516103e0516103c0516103a0516103805160065801614e56565b61046052610340610360525b6103605152602061036051036103605261014061036051101515610af257610acf565b610460516102e052610220516102e0516101e05180821015610b1357600080fd5b808203905090508082028215828483041417610b2e57600080fd5b809050905090506101e0518080610b4457600080fd5b82049050905061030052610b76565b600160c052602060c0206102405181556102605160018201555061028051610300525b6044356103005110151515610bca576308c379a0610320526020610340526014610360527f536c697070616765207363726577656420796f750000000000000000000000006103805261036050606461033cfd5b61032060006002818352015b600060046103205160028110610beb57600080fd5b60200201351115610d7257600060046103a0527f23b872dd000000000000000000000000000000000000000000000000000000006103c0526103a060048060208461040001018260208501600060045af1505080518201915050336020826104000101526020810190503060208261040001015260208101905060046103205160028110610c7857600080fd5b6020020135602082610400010152602081019050806104005261040090508051602001806104c08284600060045af1610cb057600080fd5b505060206105a06104c0516104e060006103205160028110610cd157600080fd5b600060c052602060c02001545af1610ce857600080fd5b60203d80821115610cf95780610cfb565b815b90509050610580526105808051602001806103408284600060045af1610d2057600080fd5b50506000610340511115610d7257610340806020015160008251806020901315610d4957600080fd5b8091901215610d5757600080fd5b806020036101000a82049050905090501515610d7257600080fd5b8151600101808352811415610bd6575b505060206103c060446340c10f19610320523361034052610300516103605261033c6000610200515af1610db557600080fd5b601f3d11610dc257600080fd5b6000506103c05060406004610320376102a051610360526102c05161038052610280516103a0526102205161030051818183011015610e0057600080fd5b808201905090506103c052337f26f55a85081d24974e85c6c00045d0f0453991e95873f52bff0d21af4079a76860c0610320a261030051600052600060165560206000f35b635e0d443f81141561112b5760043580806000811215610e6157195b607f1c15610e6e57600080fd5b90505060243580806000811215610e8157195b607f1c15610e8e57600080fd5b9050506101405161016051600658016147e1565b610180526101605261014052610180516101a0526101405161016051610180516101a05160065801614adf565b6101c0526101a0526101805261016052610140526101c0516101e0526101a051610140526101e051610160526101405161016051610180516101a051610160516101c0526101c0516006580161483a565b61022052610240526101a05261018052610160526101405261022080516101805280602001516101a0525061018060043560028110610f5e57600080fd5b602002015160443561014060043560028110610f7957600080fd5b60200201518082028215828483041417610f9257600080fd5b80905090509050670de0b6b3a764000080820490509050818183011015610fb857600080fd5b808201905090506101c0526101405161016051610180516101a0516101c0516101e05160406004610200376101c0516102405261018051610260526101a0516102805261028051610260516102405161022051610200516006580161514c565b6102e0526101e0526101c0526101a0526101805261016052610140526102e0516101e0526101806024356002811061104f57600080fd5b60200201516101e0518082101561106557600080fd5b8082039050905060018082101561107b57600080fd5b80820390509050610200526002546102005180820282158284830414176110a157600080fd5b809050905090506402540be40080820490509050610220526102005161022051808210156110ce57600080fd5b80820390509050670de0b6b3a764000080820282158284830414176110f257600080fd5b809050905090506101406024356002811061110c57600080fd5b6020020151808061111c57600080fd5b82049050905060005260206000f35b6307211ef7811415611775576004358080600081121561114757195b607f1c1561115457600080fd5b9050506024358080600081121561116757195b607f1c1561117457600080fd5b9050506101405160065801614adf565b61016052610140526101605161014052610140516101605161018051610140516101a0526101a0516006580161483a565b6102005261022052610180526101605261014052610200805161016052806020015161018052506007546101a0526004356001808203808060008112156111f857195b607f1c1561120557600080fd5b9050905090506101c05260243560018082038080600081121561122457195b607f1c1561123157600080fd5b9050905090506101e05260016102005260016102205260006101c051121561125b57600435610200525b60006101e051121561126f57602435610220525b60006102405260006101c051121561133f576101606004356002811061129457600080fd5b60200201516044356101405161016051610180516101a0516101c0516101e051610200516102205161024051600658016147e1565b610260526102405261022052610200526101e0526101c0526101a05261018052610160526101405261026051808202821582848304141761130957600080fd5b80905090509050670de0b6b3a76400008082049050905081818301101561132f57600080fd5b80820190509050610240526114f0565b60006101e051121561149a57606036610260376044356102606101c0516003811061136957600080fd5b602002015260206103a06084633883e1196102c052610260516102e05261028051610300526102a051610320526001610340526102dc6101a0515afa6113ae57600080fd5b601f3d116113bb57600080fd5b6000506103a0516101405180820282158284830414176113da57600080fd5b80905090509050670de0b6b3a764000080820490509050610240526102408051610240516020610320600463ddca3f436102c0526102dc6101a0515afa61142057600080fd5b601f3d1161142d57600080fd5b60005061032051808202821582848304141761144857600080fd5b809050905090506404a817c800808204905090508082101561146957600080fd5b8082039050905081525061024080516101805181818301101561148b57600080fd5b808201905090508152506114f0565b60206103206064635e0d443f610260526101c051610280526101e0516102a0526044356102c05261027c6101a0515afa6114d357600080fd5b601f3d116114e057600080fd5b6000506103205160005260206000f35b6101405161016051610180516101a0516101c0516101e051610200516102205161024051610260516102005161028052610220516102a052610240516102c052610160516102e0526101805161030052610300516102e0516102c0516102a051610280516006580161514c565b61036052610260526102405261022052610200526101e0526101c0526101a052610180526101605261014052610360516102605261016061022051600281106115a557600080fd5b602002015161026051808210156115bb57600080fd5b808203905090506001808210156115d157600080fd5b8082039050905061028052610280516002546102805180820282158284830414176115fb57600080fd5b809050905090506402540be400808204905090508082101561161c57600080fd5b808203905090506102805260243515156116d75761028051670de0b6b3a7640000808202821582848304141761165157600080fd5b809050905090506101405161016051610180516101a0516101c0516101e0516102005161022051610240516102605161028051600658016147e1565b6102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526102a05180806116cc57600080fd5b820490509050610280525b60006101e051121515611768576020610340604463cc2b27d76102a05261028051670de0b6b3a7640000808202821582848304141761171557600080fd5b8090509050905061014051808061172b57600080fd5b8204905090506102c0526101e0516102e0526102bc6101a0515afa61174f57600080fd5b601f3d1161175c57600080fd5b60005061034051610280525b6102805160005260206000f35b633df02124811415611f7c576016541561178e57600080fd5b6001601655600435808060008112156117a357195b607f1c156117b057600080fd5b905050602435808060008112156117c357195b607f1c156117d057600080fd5b905050601454156117e057600080fd5b6101405161016051600658016147e1565b610180526101605261014052610180516101a0526101405161016051610180516101a05160065801614a4f565b6101c0526101a0526101805261016052610140526101c0516101e0526101a051610140526101e0516101605260018060c052602060c020546101805260018160c052602060c02001546101a052506101405161016051610180516101a0516101c0516101e0516101405161020052610160516102205261018051610240526101a05161026052610260516102405161022051610200516006580161495b565b6102c0526102e0526101e0526101c0526101a0526101805261016052610140526102c080516101c05280602001516101e052506101c06004356002811061190357600080fd5b60200201516044356101406004356002811061191e57600080fd5b6020020151808202821582848304141761193757600080fd5b80905090509050670de0b6b3a76400008082049050905081818301101561195d57600080fd5b80820190509050610200526101405161016051610180516101a0516101c0516101e0516102005161022051604060046102403761020051610280526101c0516102a0526101e0516102c0526102c0516102a0516102805161026051610240516006580161514c565b6103205261022052610200526101e0526101c0526101a05261018052610160526101405261032051610220526101c060243560028110611a0457600080fd5b60200201516102205180821015611a1a57600080fd5b80820390509050600180821015611a3057600080fd5b8082039050905061024052610240516002548082028215828483041417611a5657600080fd5b809050905090506402540be4008082049050905061026052610240516102605180821015611a8357600080fd5b80820390509050670de0b6b3a76400008082028215828483041417611aa757600080fd5b8090509050905061014060243560028110611ac157600080fd5b60200201518080611ad157600080fd5b820490509050610240526064356102405110151515611b2f576308c379a06102805260206102a05260176102c0527f546f6f2066657720636f696e7320696e20726573756c740000000000000000006102e0526102c050606461029cfd5b610260516003548082028215828483041417611b4a57600080fd5b809050905090506402540be400808204905090506102805261028051670de0b6b3a76400008082028215828483041417611b8357600080fd5b8090509050905061014060243560028110611b9d57600080fd5b60200201518080611bad57600080fd5b8204905090506102805261018060043560028110611bca57600080fd5b6020020151604435818183011015611be157600080fd5b8082019050905060043560028110611bf857600080fd5b600160c052602060c020015561018060243560028110611c1757600080fd5b60200201516102405180821015611c2d57600080fd5b808203905090506102805180821015611c4557600080fd5b8082039050905060243560028110611c5c57600080fd5b600160c052602060c020015560006004610300527f23b872dd000000000000000000000000000000000000000000000000000000006103205261030060048060208461036001018260208501600060045af15050805182019150503360208261036001015260208101905030602082610360010152602081019050604435602082610360010152602081019050806103605261036090508051602001806104208284600060045af1611d0d57600080fd5b5050602061050061042051610440600060043560028110611d2d57600080fd5b600060c052602060c02001545af1611d4457600080fd5b60203d80821115611d555780611d57565b815b905090506104e0526104e08051602001806102a08284600060045af1611d7c57600080fd5b505060006102a0511115611dce576102a0806020015160008251806020901315611da557600080fd5b8091901215611db357600080fd5b806020036101000a82049050905090501515611dce57600080fd5b60006004610300527fa9059cbb000000000000000000000000000000000000000000000000000000006103205261030060048060208461036001018260208501600060045af15050805182019150503360208261036001015260208101905061024051602082610360010152602081019050806103605261036090508051602001806104008284600060045af1611e6457600080fd5b505060206104c061040051610420600060243560028110611e8457600080fd5b600060c052602060c02001545af1611e9b57600080fd5b60203d80821115611eac5780611eae565b815b905090506104a0526104a08051602001806102a08284600060045af1611ed357600080fd5b505060006102a0511115611f25576102a0806020015160008251806020901315611efc57600080fd5b8091901215611f0a57600080fd5b806020036101000a82049050905090501515611f2557600080fd5b6004356103005260443561032052602435610340526102405161036052337f8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd971406080610300a261024051600052600060165560206000f35b63a6417ed6811415612d605760165415611f9557600080fd5b600160165560043580806000811215611faa57195b607f1c15611fb757600080fd5b90505060243580806000811215611fca57195b607f1c15611fd757600080fd5b90505060145415611fe757600080fd5b6101405161016051600658016147e1565b610180526101605261014052610180516101a0526101405161016051610180516101a05160065801614a4f565b6101c0526101a0526101805261016052610140526101c0516101e0526101a051610140526101e051610160526007546101805260043560018082038080600081121561206d57195b607f1c1561207a57600080fd5b9050905090506101a05260243560018082038080600081121561209957195b607f1c156120a657600080fd5b9050905090506101c05260016101e05260016102005260006101a05112156120d0576004356101e0525b60006101c05112156120e457602435610200525b6060366102203760006101a051121561211c576004356002811061210757600080fd5b600060c052602060c02001546102405261213e565b6101a0516003811061212d57600080fd5b600a60c052602060c0200154610240525b60006101c051121561216f576024356002811061215a57600080fd5b600060c052602060c020015461026052612191565b6101c0516003811061218057600080fd5b600a60c052602060c0200154610260525b6044356102805273dac17f958d2ee523a2206206994597c13d831ec761024051141561220857602061032060246370a082316102a052306102c0526102bc73dac17f958d2ee523a2206206994597c13d831ec75afa6121ef57600080fd5b601f3d116121fc57600080fd5b60005061032051610280525b60006004610300527f23b872dd000000000000000000000000000000000000000000000000000000006103205261030060048060208461036001018260208501600060045af15050805182019150503360208261036001015260208101905030602082610360010152602081019050604435602082610360010152602081019050806103605261036090508051602001806104208284600060045af16122ad57600080fd5b50506020610500610420516104406000610240515af16122cc57600080fd5b60203d808211156122dd57806122df565b815b905090506104e0526104e08051602001806102a08284600060045af161230457600080fd5b505060006102a0511115612356576102a080602001516000825180602090131561232d57600080fd5b809190121561233b57600080fd5b806020036101000a8204905090509050151561235657600080fd5b73dac17f958d2ee523a2206206994597c13d831ec76102405114156123de57602061038060246370a0823161030052306103205261031c73dac17f958d2ee523a2206206994597c13d831ec75afa6123ad57600080fd5b601f3d116123ba57600080fd5b6000506103805161028051808210156123d257600080fd5b80820390509050610280525b60006101a05112156123f15760016123f9565b60006101c051125b15612ae35760018060c052602060c020546103005260018160c052602060c02001546103205250610140610380525b6103805151602061038051016103805261038061038051101561244a57612428565b610140516103a052610160516103c052610300516103e0526103205161040052610400516103e0516103c0516103a0516006580161495b565b6104605261048052610360610380525b61038051526020610380510361038052610140610380511015156124b657612493565b6104608051610340528060200151610360525060006103805260006101a051121561255957610340600435600281106124ee57600080fd5b6020020151610280516101406004356002811061250a57600080fd5b6020020151808202821582848304141761252357600080fd5b80905090509050670de0b6b3a76400008082049050905081818301101561254957600080fd5b80820190509050610380526126ce565b6060366103a037610280516103a06101a0516003811061257857600080fd5b60200201526001600060c052602060c02001546104005260206104a060246370a0823161042052306104405261043c610400515afa6125b657600080fd5b601f3d116125c357600080fd5b6000506104a05161038052610180513b6125dc57600080fd5b600060006084634515cef3610420526103a051610440526103c051610460526103e0516104805260006104a05261043c6000610180515af161261d57600080fd5b60206104a060246370a0823161042052306104405261043c610400515afa61264457600080fd5b601f3d1161265157600080fd5b6000506104a051610380518082101561266957600080fd5b80820390509050610280526102805161016051808202821582848304141761269057600080fd5b80905090509050670de0b6b3a764000080820490509050610380526103808051610360518181830110156126c357600080fd5b808201905090508152505b6101406103c0525b6103c0515160206103c051016103c0526103c06103c05110156126f8576126d6565b6101e0516103e0526102005161040052610380516104205261034051610440526103605161046052610460516104405161042051610400516103e0516006580161514c565b6104c0526103a06103c0525b6103c0515260206103c051036103c0526101406103c05110151561276c57612749565b6104c0516103a052610340610200516002811061278857600080fd5b60200201516103a0518082101561279e57600080fd5b808203905090506001808210156127b457600080fd5b80820390509050610220526102205160025480820282158284830414176127da57600080fd5b809050905090506402540be400808204905090506103c052610220516103c0518082101561280757600080fd5b80820390509050670de0b6b3a7640000808202821582848304141761282b57600080fd5b80905090509050610140610200516002811061284657600080fd5b6020020151808061285657600080fd5b820490509050610220526103c051600354808202821582848304141761287b57600080fd5b809050905090506402540be400808204905090506103e0526103e051670de0b6b3a764000080820282158284830414176128b457600080fd5b8090509050905061014061020051600281106128cf57600080fd5b602002015180806128df57600080fd5b8204905090506103e0526103006101e051600281106128fd57600080fd5b60200201516102805181818301101561291557600080fd5b808201905090506101e0516002811061292d57600080fd5b600160c052602060c0200155610300610200516002811061294d57600080fd5b6020020151610220518082101561296357600080fd5b808203905090506103e0518082101561297b57600080fd5b80820390509050610200516002811061299357600080fd5b600160c052602060c020015560006101c051121515612a8a5760206104a060246370a0823161042052306104405261043c610260515afa6129d357600080fd5b601f3d116129e057600080fd5b6000506104a05161040052610180513b6129f957600080fd5b600060006064631a4d01d26104205261022051610440526101c0516104605260006104805261043c6000610180515af1612a3257600080fd5b60206104a060246370a0823161042052306104405261043c610260515afa612a5957600080fd5b601f3d11612a6657600080fd5b6000506104a0516104005180821015612a7e57600080fd5b80820390509050610220525b6064356102205110151515612ade576308c379a0610400526020610420526017610440527f546f6f2066657720636f696e7320696e20726573756c740000000000000000006104605261044050606461041cfd5b612bca565b602061038060246370a0823161030052306103205261031c610260515afa612b0a57600080fd5b601f3d11612b1757600080fd5b6000506103805161022052610180513b612b3057600080fd5b600060006084633df02124610300526101a051610320526101c0516103405261028051610360526064356103805261031c6000610180515af1612b7257600080fd5b602061038060246370a0823161030052306103205261031c610260515afa612b9957600080fd5b601f3d11612ba657600080fd5b600050610380516102205180821015612bbe57600080fd5b80820390509050610220525b60006004610300527fa9059cbb000000000000000000000000000000000000000000000000000000006103205261030060048060208461036001018260208501600060045af15050805182019150503360208261036001015260208101905061022051602082610360010152602081019050806103605261036090508051602001806104008284600060045af1612c6057600080fd5b505060206104c0610400516104206000610260515af1612c7f57600080fd5b60203d80821115612c905780612c92565b815b905090506104a0526104a08051602001806102a08284600060045af1612cb757600080fd5b505060006102a0511115612d09576102a0806020015160008251806020901315612ce057600080fd5b8091901215612cee57600080fd5b806020036101000a82049050905090501515612d0957600080fd5b6004356103005260443561032052602435610340526102205161036052337fd013ca23e77a65003c2c659c5442c00c805371b7fc1ebd4c206c41d1536bd90b6080610300a261022051600052600060165560206000f35b635b36389c8114156130315760165415612d7957600080fd5b60016016556005546101405260206101e060046318160ddd6101805261019c610140515afa612da757600080fd5b601f3d11612db457600080fd5b6000506101e05161016052604036610180376101c060006002818352015b6101c05160028110612de357600080fd5b600160c052602060c02001546101e0526101e0516004358082028215828483041417612e0e57600080fd5b80905090509050610160518080612e2457600080fd5b8204905090506102005260246101c05160028110612e4157600080fd5b60200201356102005110151515612ebc576308c379a0610220526020610240526030610260527f5769746864726177616c20726573756c74656420696e20666577657220636f69610280527f6e73207468616e206578706563746564000000000000000000000000000000006102a05261026050608461023cfd5b6101e0516102005180821015612ed157600080fd5b808203905090506101c05160028110612ee957600080fd5b600160c052602060c0200155610200516101806101c05160028110612f0d57600080fd5b602002015260206102c0604463a9059cbb610220523361024052610200516102605261023c60006101c05160028110612f4557600080fd5b600060c052602060c02001545af1612f5c57600080fd5b601f3d11612f6957600080fd5b6000506102c0505b8151600101808352811415612dd2575b5050602061026060446379cc67906101c052336101e052600435610200526101dc6000610140515af1612fb357600080fd5b601f3d11612fc057600080fd5b60005061026050610180516101c0526101a0516101e052604036610200376101605160043580821015612ff257600080fd5b8082039050905061024052337f7c363854ccf79623411f8995b362bce5eddff18c927edc6f5dbbb5e05819a82c60a06101c0a260006016556040610180f35b63e3103273811415613867576016541561304a57600080fd5b60016016556014541561305c57600080fd5b6101405160065801614500565b61016052610140526101605161014052610140516101605161018051600658016147e1565b6101a0526101805261016052610140526101a0516101c0526101405161016051610180516101a0516101c05160065801614adf565b6101e0526101c0526101a0526101805261016052610140526101e051610200526101c05161016052610200516101805260018060c052602060c020546101a05260018160c052602060c02001546101c052506101a0516101e0526101c051610200526101405161016051610180516101a0516101c0516101e0516102005161022051610160516102405261018051610260526101a051610280526101c0516102a052610140516102c0526102c0516102a05161028051610260516102405160065801614e56565b6103205261022052610200526101e0526101c0526101a052610180526101605261014052610320516102205261024060006002818352015b6101e061024051600281106131d657600080fd5b602002018051600461024051600281106131ef57600080fd5b60200201358082101561320157600080fd5b808203905090508152505b81516001018083528114156131c2575b50506101405161016051610180516101a0516101c0516101e051610200516102205161024051610160516102605261018051610280526101e0516102a052610200516102c052610140516102e0526102e0516102c0516102a051610280516102605160065801614e56565b610340526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103405161024052600254600280820282158284830414176132d057600080fd5b8090509050905060048082049050905061026052600354610280526040366102a0376102e060006002818352015b610240516101a06102e0516002811061331657600080fd5b6020020151808202821582848304141761332f57600080fd5b8090509050905061022051808061334557600080fd5b820490509050610300526000610320526101e06102e0516002811061336957600080fd5b60200201516103005111156133b257610300516101e06102e0516002811061339057600080fd5b6020020151808210156133a257600080fd5b80820390509050610320526133e8565b6101e06102e051600281106133c657600080fd5b602002015161030051808210156133dc57600080fd5b80820390509050610320525b6102605161032051808202821582848304141761340457600080fd5b809050905090506402540be400808204905090506102a06102e0516002811061342c57600080fd5b60200201526101e06102e0516002811061344557600080fd5b60200201516102a06102e0516002811061345e57600080fd5b602002015161028051808202821582848304141761347b57600080fd5b809050905090506402540be400808204905090508082101561349c57600080fd5b808203905090506102e051600281106134b457600080fd5b600160c052602060c02001556101e06102e051600281106134d457600080fd5b6020020180516102a06102e051600281106134ee57600080fd5b60200201518082101561350057600080fd5b808203905090508152505b81516001018083528114156132fe575b50506101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e051610160516103005261018051610320526101e0516103405261020051610360526101405161038052610380516103605161034051610320516103005160065801614e56565b6103e0526102e0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103e0516102e0526005546103005260206103a060046318160ddd6103405261035c610300515afa61360757600080fd5b601f3d1161361457600080fd5b6000506103a05161032052610220516102e0518082101561363457600080fd5b8082039050905061032051808202821582848304141761365357600080fd5b8090509050905061022051808061366957600080fd5b820490509050610340526000610340511861368357600080fd5b6103408051600181818301101561369957600080fd5b8082019050905081525060443561034051111515156136f7576308c379a06103605260206103805260146103a0527f536c697070616765207363726577656420796f750000000000000000000000006103c0526103a050606461037cfd5b602061040060446379cc6790610360523361038052610340516103a05261037c6000610300515af161372857600080fd5b601f3d1161373557600080fd5b6000506104005061036060006002818352015b60006004610360516002811061375d57600080fd5b602002013518156137db576020610420604463a9059cbb61038052336103a0526004610360516002811061379057600080fd5b60200201356103c05261039c600061036051600281106137af57600080fd5b600060c052602060c02001545af16137c657600080fd5b601f3d116137d357600080fd5b600050610420505b8151600101808352811415613748575b505060406004610360376102a0516103a0526102c0516103c052610240516103e05261032051610340518082101561382257600080fd5b8082039050905061040052337f2b5508378d7e19e0d5fa338419034731416c4f5b219a10379956f764317fd47e60c0610360a261034051600052600060165560206000f35b63cc2b27d7811415613926576024358080600081121561388357195b607f1c1561389057600080fd5b9050506101405160065801614adf565b61016052610140526101605161014052610140516040600461016037610140516101a0526101a051610180516101605160065801615920565b61020052610220526102405261014052610200808080805161026052505060208101905080808051610280525050602081019050808080516102a052505050506102605160005260206000f35b631a4d01d2811415613c0b576016541561393f57600080fd5b60016016556024358080600081121561395457195b607f1c1561396157600080fd5b9050506014541561397157600080fd5b6101405160065801614a4f565b61016052610140526101605161014052606036610160376101405161016051610180516101a051604060046101c0376101405161020052610200516101e0516101c05160065801615920565b61026052610280526102a0526101a05261018052610160526101405261026080808080516102c0525050602081019050808080516102e05250506020810190508080805161030052505050506102c080516101605280602001516101805280604001516101a052506044356101605110151515613a86576308c379a06101c05260206101e0526018610200527f4e6f7420656e6f75676820636f696e732072656d6f7665640000000000000000610220526102005060646101dcfd5b60243560028110613a9657600080fd5b600160c052602060c02001805461016051610180516003548082028215828483041417613ac257600080fd5b809050905090506402540be40080820490509050818183011015613ae557600080fd5b8082019050905080821015613af957600080fd5b80820390509050815550602061026060446379cc67906101c052336101e052600435610200526101dc60006005545af1613b3257600080fd5b601f3d11613b3f57600080fd5b600050610260506020610260604463a9059cbb6101c052336101e05261016051610200526101dc600060243560028110613b7857600080fd5b600060c052602060c02001545af1613b8f57600080fd5b601f3d11613b9c57600080fd5b600050610260506004356101c052610160516101e0526101a05160043580821015613bc657600080fd5b8082039050905061020052337f5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a060606101c0a261016051600052600060165560206000f35b633c157e64811415613dac576004543314613c2557600080fd5b600d5462015180818183011015613c3b57600080fd5b80820190509050421015613c4e57600080fd5b4262015180818183011015613c6257600080fd5b808201905090506024351015613c7757600080fd5b6101405160065801614500565b6101605261014052610160516101405260043560648082028215828483041417613cad57600080fd5b809050905090506101605260006004351115613cd057620f424060043510613cd3565b60005b613cdc57600080fd5b61014051610160511015613d1f576101405161016051600a8082028215828483041417613d0857600080fd5b809050905090501015613d1a57600080fd5b613d4f565b61014051600a8082028215828483041417613d3957600080fd5b80905090509050610160511115613d4f57600080fd5b61014051600b5561016051600c5542600d55602435600e556101405161018052610160516101a052426101c0526024356101e0527fa2b71ec6df949300b59aab36b55e189697b750119dd349fcfa8c0f779e83c2546080610180a1005b63551a6588811415613e2f576004543314613dc657600080fd5b6101405160065801614500565b6101605261014052610160516101405261014051600b5561014051600c5542600d5542600e55610140516101605242610180527f46e22fb3709ad289f62ce63d469248536dbc78d82b84a3d7e74ad606dc2019386040610160a1005b635b5a1467811415613eeb576004543314613e4957600080fd5b600f5415613e5657600080fd5b64012a05f2006004351115613e6a57600080fd5b6402540be4006024351115613e7e57600080fd5b426203f480818183011015613e9257600080fd5b808201905090506101405261014051600f556004356011556024356012556004356101605260243561018052610140517f351fc5da2fbf480f2225debf3664a4bc90fa9923743aad58b4603f648e931fe06040610160a2005b634f12fe97811415613f7d576004543314613f0557600080fd5b600f54421015613f1457600080fd5b6000600f5418613f2357600080fd5b6000600f55601154610140526012546101605261014051600255610160516003556101405161018052610160516101a0527fbe12859b636aed607d5230b2cc2711f68d70e51060e6cca1f575ef5d2fcc95d16040610180a1005b63226840fb811415613f9e576004543314613f9757600080fd5b6000600f55005b636b441a408114156140305760043560a01c15613fba57600080fd5b6004543314613fc857600080fd5b60105415613fd557600080fd5b426203f480818183011015613fe957600080fd5b808201905090506101405261014051601055600435601355600435610140517f181aa3aa17d4cbf99265dd4443eba009433d3cde79d60164fde1d1a192beb93560006000a3005b636a1c05ae8114156140a757600454331461404a57600080fd5b60105442101561405957600080fd5b60006010541861406857600080fd5b60006010556013546101405261014051600455610140517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c60006000a2005b6386fbf1938114156140c85760045433146140c157600080fd5b6000601055005b63e2e7d2648114156141605760206101c060246370a0823161014052306101605261015c600435600281106140fc57600080fd5b600060c052602060c02001545afa61411357600080fd5b601f3d1161412057600080fd5b6000506101c0516004356002811061413757600080fd5b600160c052602060c02001548082101561415057600080fd5b8082039050905060005260206000f35b6330c5408581141561427d57600454331461417a57600080fd5b61014060006002818352015b610140516002811061419757600080fd5b600060c052602060c020015461016052602061022060246370a082316101a052306101c0526101bc610160515afa6141ce57600080fd5b601f3d116141db57600080fd5b6000506102205161014051600281106141f357600080fd5b600160c052602060c02001548082101561420c57600080fd5b80820390509050610180526000610180511115614269576020610240604463a9059cbb6101a052336101c052610180516101e0526101bc6000610160515af161425457600080fd5b601f3d1161426157600080fd5b600050610240505b8151600101808352811415614186575b5050005b63e36988538114156142ac57600454331461429757600080fd5b42601554116142a557600080fd5b6001601455005b633046f9728114156142cd5760045433146142c657600080fd5b6000601455005b63c66106578114156142fe57600435600281106142e957600080fd5b600060c052602060c020015460005260206000f35b634903b0d181141561432f576004356002811061431a57600080fd5b600160c052602060c020015460005260206000f35b63ddca3f438114156143475760025460005260206000f35b63fee3f7f981141561435f5760035460005260206000f35b638da5cb5b8114156143775760045460005260206000f35b6382c6306681141561438f5760055460005260206000f35b6385cab7558114156143a75760065460005260206000f35b635d6362bb8114156143bf5760075460005260206000f35b6395ccc02f8114156143d75760085460005260206000f35b638296f84f8114156143ef5760095460005260206000f35b6387cb4f57811415614420576004356003811061440b57600080fd5b600a60c052602060c020015460005260206000f35b635409491a81141561443857600b5460005260206000f35b63b4b577ad81141561445057600c5460005260206000f35b632081066c81141561446857600d5460005260206000f35b631405228881141561448057600e5460005260206000f35b63405e28f881141561449857600f5460005260206000f35b63e0a0b5868114156144b05760105460005260206000f35b6358680d0b8114156144c85760115460005260206000f35b63e38244628114156144e05760125460005260206000f35b631ec0cdc18114156144f85760135460005260206000f35b505b60006000fd5b61014052600e5461016052600c54610180526101605142101561468657600b546101a052600d546101c0526101a0516101805111156145e0576101a051610180516101a0518082101561455257600080fd5b80820390509050426101c0518082101561456b57600080fd5b80820390509050808202821582848304141761458657600080fd5b80905090509050610160516101c051808210156145a257600080fd5b8082039050905080806145b457600080fd5b8204905090508181830110156145c957600080fd5b808201905090506000526000516101405156614681565b6101a0516101a05161018051808210156145f957600080fd5b80820390509050426101c0518082101561461257600080fd5b80820390509050808202821582848304141761462d57600080fd5b80905090509050610160516101c0518082101561464957600080fd5b80820390509050808061465b57600080fd5b8204905090508082101561466e57600080fd5b8082039050905060005260005161014051565b614696565b6101805160005260005161014051565b005b61016052610140526101405115156146b857600060005260005161016051565b61014051670de0b6b3a76400008181830110156146d457600080fd5b8082019050905060028082049050905061018052610140516101a0526101c06000610100818352015b6101a05161018051141561471d576101a051600052505060005161016051565b610180516101a05261014051670de0b6b3a7640000808202821582848304141761474657600080fd5b8090509050905061018051808061475c57600080fd5b8204905090506101805181818301101561477557600080fd5b80820190509050600280820490509050610180525b81516001018083528114156146fd575b50506308c379a06101c05260206101e0526010610200527f446964206e6f7420636f6e766572676500000000000000000000000000000000610220526102005060646101dcfd5b6101405260206101e06004636998c48d6101805261019c6006545afa61480657600080fd5b601f3d1161481357600080fd5b6000506101e0516101605261016051633b9aca008082049050905060005260005161014051565b61016052610140526101405161016051610180516101a051600658016147e1565b6101c0526101a0526101805261016052610140526101c0516101e0526101e05161018052610140516101a0526101c060006002818352015b6101806101c051600281106148a757600080fd5b60200201516101c051600281106148bd57600080fd5b600160c052602060c020015480820282158284830414176148dd57600080fd5b80905090509050670de0b6b3a7640000808204905090506101806101c0516002811061490857600080fd5b60200201525b8151600101808352811415614893575b505060406101c0525b60006101c05111151561493957614955565b60206101c05103610180015160206101c051036101c052614927565b61016051565b6101c0526101405261016052610180526101a052610140516101e052610160516102005261022060006002818352015b6101e0610220516002811061499f57600080fd5b602002015161018061022051600281106149b857600080fd5b602002015180820282158284830414176149d157600080fd5b80905090509050670de0b6b3a7640000808204905090506101e061022051600281106149fc57600080fd5b60200201525b815160010180835281141561498b575b50506040610220525b600061022051111515614a2d57614a49565b602061022051036101e001516020610220510361022052614a1b565b6101c051565b61014052600954610258818183011015614a6857600080fd5b80820190509050421115614ace5760206101e0600463bb7b8b806101805261019c6007545afa614a9757600080fd5b601f3d11614aa457600080fd5b6000506101e051610160526101605160085542600955610160516000526000516101405156614add565b60085460005260005161014051565b005b61014052600954610258818183011015614af857600080fd5b80820190509050421115614b4b5760206101c0600463bb7b8b806101605261017c6007545afa614b2757600080fd5b601f3d11614b3457600080fd5b6000506101c0516000526000516101405156614b5a565b60085460005260005161014051565b005b6101a0526101405261016052610180526040366101c03761022060006002818352015b602061022051026101400151610200526101c0805161020051818183011015614ba757600080fd5b808201905090508152505b8151600101808352811415614b7f575b50506101c0511515614bdc5760006000526000516101a051565b6101c051610200526101805160028082028215828483041417614bfe57600080fd5b8090509050905061022052610240600060ff818352015b61020051610260526102a060006002818352015b60206102a0510261014001516102805261026051610200518082028215828483041417614c5557600080fd5b809050905090506102805160028082028215828483041417614c7657600080fd5b809050905090508080614c8857600080fd5b820490509050610260525b8151600101808352811415614c29575b5050610200516101e052610220516101c0518082028215828483041417614cc957600080fd5b809050905090506064808204905090506102605160028082028215828483041417614cf357600080fd5b80905090509050818183011015614d0957600080fd5b80820190509050610200518082028215828483041417614d2857600080fd5b8090509050905061022051606480821015614d4257600080fd5b80820390509050610200518082028215828483041417614d6157600080fd5b809050905090506064808204905090506003610260518082028215828483041417614d8b57600080fd5b80905090509050818183011015614da157600080fd5b808201905090508080614db357600080fd5b820490509050610200526101e051610200511115614e07576001610200516101e05180821015614de257600080fd5b80820390509050111515614e02576102005160005250506000516101a051565b614e3e565b60016101e0516102005180821015614e1e57600080fd5b80820390509050111515614e3e576102005160005250506000516101a051565b8151600101808352811415614c15575b505060006000fd5b6101e0526101405261016052610180526101a0526101c0526101405161016051610180516101a0516101c0516101e0516101405161020052610160516102205261018051610240526101a05161026052610260516102405161022051610200516006580161495b565b6102c0526102e0526101e0526101c0526101a0526101805261016052610140526102c0805161030052806020015161032052506101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e0516103005161032051610300516103405261032051610360526101c0516103805261038051610360516103405160065801614b5c565b6103e05261032052610300526102e0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103e0516000526000516101e051565b61014052610140516101605160065801614500565b610180526101605261014052610180516101605261014051610160516101805160065801614adf565b6101a0526101805261016052610140526101a051610180526101405161016051610180516101a0516101c051610180516101e0526101e0516006580161483a565b61024052610260526101c0526101a05261018052610160526101405261024080516101a05280602001516101c052506101405161016051610180516101a0516101c0516101e0516101a051610200526101c05161022052610160516102405261024051610220516102005160065801614b5c565b6102a0526101e0526101c0526101a0526101805261016052610140526102a0516101e052602061028060046318160ddd6102205261023c6005545afa6150eb57600080fd5b601f3d116150f857600080fd5b60005061028051610200526101e051670de0b6b3a7640000808202821582848304141761512457600080fd5b8090509050905061020051808061513a57600080fd5b82049050905060005260005161014051565b6101e0526101405261016052610180526101a0526101c05261016051610140511861517657600080fd5b600061016051121561518757600080fd5b6002610160511261519757600080fd5b60006101405112156151a857600080fd5b600261014051126151b857600080fd5b6101405161016051610180516101a0516101c0516101e0516102005160065801614500565b61022052610200526101e0526101c0526101a05261018052610160526101405261022051610200526101405161016051610180516101a0516101c0516101e05161020051610220516101a051610240526101c05161026052610200516102805261028051610260516102405160065801614b5c565b6102e05261022052610200526101e0526101c0526101a0526101805261016052610140526102e05161022052610200516002808202821582848304141761529857600080fd5b80905090509050610240526102205161026052606036610280376102e060006002818352015b610140516102e05114156152d957610180516102a05261530e565b610160516102e0511815615309576101a06102e051600281106152fb57600080fd5b60200201516102a05261530e565b61538a565b61028080516102a05181818301101561532657600080fd5b808201905090508152506102605161022051808202821582848304141761534c57600080fd5b809050905090506102a0516002808202821582848304141761536d57600080fd5b80905090509050808061537f57600080fd5b820490509050610260525b81516001018083528114156152be575b5050610260516102205180820282158284830414176153b857600080fd5b80905090509050606480820282158284830414176153d557600080fd5b8090509050905061024051600280820282158284830414176153f657600080fd5b80905090509050808061540857600080fd5b8204905090506102605261028051610220516064808202821582848304141761543057600080fd5b8090509050905061024051808061544657600080fd5b82049050905081818301101561545b57600080fd5b808201905090506102e0526102205161030052610320600060ff818352015b610300516102c0526103005161030051808202821582848304141761549e57600080fd5b80905090509050610260518181830110156154b857600080fd5b8082019050905060026103005180820282158284830414176154d957600080fd5b809050905090506102e0518181830110156154f357600080fd5b80820190509050610220518082101561550b57600080fd5b80820390509050808061551d57600080fd5b820490509050610300526102c051610300511115615571576001610300516102c0518082101561554c57600080fd5b8082039050905011151561556c576103005160005250506000516101e051565b6155a8565b60016102c051610300518082101561558857600080fd5b808203905090501115156155a8576103005160005250506000516101e051565b815160010180835281141561547a575b505060006000fd5b6101e0526101405261016052610180526101a0526101c05260006101605112156155e957600080fd5b600261016051126155f957600080fd5b610140516002808202821582848304141761561357600080fd5b80905090509050610200526101c05161022052606036610240376102a060006002818352015b610160516102a0511815615669576101806102a0516002811061565b57600080fd5b60200201516102605261566e565b6156ea565b61024080516102605181818301101561568657600080fd5b80820190509050815250610220516101c05180820282158284830414176156ac57600080fd5b8090509050905061026051600280820282158284830414176156cd57600080fd5b8090509050905080806156df57600080fd5b820490509050610220525b8151600101808352811415615639575b5050610220516101c051808202821582848304141761571857600080fd5b809050905090506064808202821582848304141761573557600080fd5b80905090509050610200516002808202821582848304141761575657600080fd5b80905090509050808061576857600080fd5b82049050905061022052610240516101c0516064808202821582848304141761579057600080fd5b809050905090506102005180806157a657600080fd5b8204905090508181830110156157bb57600080fd5b808201905090506102a0526101c0516102c0526102e0600060ff818352015b6102c051610280526102c0516102c05180820282158284830414176157fe57600080fd5b809050905090506102205181818301101561581857600080fd5b8082019050905060026102c051808202821582848304141761583957600080fd5b809050905090506102a05181818301101561585357600080fd5b808201905090506101c0518082101561586b57600080fd5b80820390509050808061587d57600080fd5b8204905090506102c052610280516102c05111156158d15760016102c05161028051808210156158ac57600080fd5b808203905090501115156158cc576102c05160005250506000516101e051565b615908565b6001610280516102c051808210156158e857600080fd5b80820390509050111515615908576102c05160005250506000516101e051565b81516001018083528114156157da575b505060006000fd5b6101a0526101405261016052610180526101405161016051610180516101a0516101c05160065801614500565b6101e0526101c0526101a0526101805261016052610140526101e0516101c0526101405161016051610180516101a0516101c0516101e051610200516101805161022052610220516006580161483a565b610280526102a052610200526101e0526101c0526101a05261018052610160526101405261028080516101e052806020015161020052506101405161016051610180516101a0516101c0516101e05161020051610220516101e0516102405261020051610260526101c0516102805261028051610260516102405160065801614b5c565b6102e05261022052610200526101e0526101c0526101a0526101805261016052610140526102e0516102205260206102c060046318160ddd6102605261027c6005545afa615a6f57600080fd5b601f3d11615a7c57600080fd5b6000506102c051610240526102205161014051610220518082028215828483041417615aa757600080fd5b80905090509050610240518080615abd57600080fd5b82049050905080821015615ad057600080fd5b80820390509050610260526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516101c0516102a052610160516102c0526101e0516102e0526102005161030052610260516103205261032051610300516102e0516102c0516102a051600658016155c0565b6103805261028052610260526102405261022052610200526101e0526101c0526101a052610180526101605261014052610380516102805260025460028082028215828483041417615b9d57600080fd5b809050905090506004808204905090506102a052610140610300525b61030051516020610300510161030052610300610300511015615bdb57615bb9565b600658016147e1565b610320526102e0610300525b6103005152602061030051036103005261014061030051101515615c1357615bf0565b6103205161034052610340516102c052610180516102e0526101e0516103005261020051610320526101e06101605160028110615c4f57600080fd5b60200201516102805180821015615c6557600080fd5b80820390509050670de0b6b3a76400008082028215828483041417615c8957600080fd5b809050905090506102c06101605160028110615ca457600080fd5b60200201518080615cb457600080fd5b8204905090506103405261036060006002818352015b60006103805261016051610360511415615d4c576101e06103605160028110615cf257600080fd5b6020020151610260518082028215828483041417615d0f57600080fd5b80905090509050610220518080615d2557600080fd5b8204905090506102805180821015615d3c57600080fd5b8082039050905061038052615dcb565b6101e06103605160028110615d6057600080fd5b60200201516101e06103605160028110615d7957600080fd5b6020020151610260518082028215828483041417615d9657600080fd5b80905090509050610220518080615dac57600080fd5b82049050905080821015615dbf57600080fd5b80820390509050610380525b6103006103605160028110615ddf57600080fd5b6020020180516102a051610380518082028215828483041417615e0157600080fd5b809050905090506402540be4008082049050905080821015615e2257600080fd5b808203905090508152505b8151600101808352811415615cca575b50506103006101605160028110615e5357600080fd5b6020020151610140610380525b61038051516020610380510161038052610380610380511015615e8257615e60565b6101c0516103a052610160516103c052610300516103e0526103205161040052610260516104205261042051610400516103e0516103c0516103a051600658016155c0565b61048052610360610380525b6103805152602061038051036103805261014061038051101515615ef657615ed3565b6104805180821015615f0757600080fd5b808203905090506103605261036051600180821015615f2557600080fd5b80820390509050670de0b6b3a76400008082028215828483041417615f4957600080fd5b809050905090506102c06101605160028110615f6457600080fd5b60200201518080615f7457600080fd5b820490509050610360526103e0610360518152610340516103605180821015615f9c57600080fd5b808203905090508160200152610240518160400152506060610440525b600061044051111515615fcb57615fe7565b602061044051036103e001516020610440510361044052615fb9565b6101a05156
Verified Source Code Partial Match
Compiler: v0.2.12+commit.2c6842c
Vyper_contract.vy 1160 lines
# @version 0.2.12
"""
@title StableSwap
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2021 - all rights reserved
@notice Metapool implementation
@dev Swaps between 3pool and RAI
"""
from vyper.interfaces import ERC20
interface CurveToken:
def totalSupply() -> uint256: view
def mint(_to: address, _value: uint256) -> bool: nonpayable
def burnFrom(_to: address, _value: uint256) -> bool: nonpayable
interface Curve:
def coins(i: uint256) -> address: view
def get_virtual_price() -> uint256: view
def calc_token_amount(amounts: uint256[BASE_N_COINS], deposit: bool) -> uint256: view
def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256: view
def fee() -> uint256: view
def get_dy(i: int128, j: int128, dx: uint256) -> uint256: view
def get_dy_underlying(i: int128, j: int128, dx: uint256) -> uint256: view
def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256): nonpayable
def add_liquidity(amounts: uint256[BASE_N_COINS], min_mint_amount: uint256): nonpayable
def remove_liquidity_one_coin(_token_amount: uint256, i: int128, min_amount: uint256): nonpayable
interface RedemptionPriceSnap:
def snappedRedemptionPrice() -> uint256: view
# Events
event TokenExchange:
buyer: indexed(address)
sold_id: int128
tokens_sold: uint256
bought_id: int128
tokens_bought: uint256
event TokenExchangeUnderlying:
buyer: indexed(address)
sold_id: int128
tokens_sold: uint256
bought_id: int128
tokens_bought: uint256
event AddLiquidity:
provider: indexed(address)
token_amounts: uint256[N_COINS]
fees: uint256[N_COINS]
invariant: uint256
token_supply: uint256
event RemoveLiquidity:
provider: indexed(address)
token_amounts: uint256[N_COINS]
fees: uint256[N_COINS]
token_supply: uint256
event RemoveLiquidityOne:
provider: indexed(address)
token_amount: uint256
coin_amount: uint256
token_supply: uint256
event RemoveLiquidityImbalance:
provider: indexed(address)
token_amounts: uint256[N_COINS]
fees: uint256[N_COINS]
invariant: uint256
token_supply: uint256
event CommitNewAdmin:
deadline: indexed(uint256)
admin: indexed(address)
event NewAdmin:
admin: indexed(address)
event CommitNewFee:
deadline: indexed(uint256)
fee: uint256
admin_fee: uint256
event NewFee:
fee: uint256
admin_fee: uint256
event RampA:
old_A: uint256
new_A: uint256
initial_time: uint256
future_time: uint256
event StopRampA:
A: uint256
t: uint256
N_COINS: constant(uint256) = 2
MAX_COIN: constant(uint256) = N_COINS - 1
REDEMPTION_COIN: constant(uint256) = 0 # Index of asset with moving target redemption price
REDMPTION_PRICE_SCALE: constant(uint256) = 10 ** 9
FEE_DENOMINATOR: constant(uint256) = 10 ** 10
PRECISION: constant(uint256) = 10 ** 18 # The precision to convert to
BASE_N_COINS: constant(uint256) = 3
# An asset which may have a transfer fee (USDT)
FEE_ASSET: constant(address) = 0xdAC17F958D2ee523a2206206994597C13D831ec7
MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9
MAX_FEE: constant(uint256) = 5 * 10 ** 9
MAX_A: constant(uint256) = 10 ** 6
MAX_A_CHANGE: constant(uint256) = 10
ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400
MIN_RAMP_TIME: constant(uint256) = 86400
coins: public(address[N_COINS])
balances: public(uint256[N_COINS])
fee: public(uint256) # fee * 1e10
admin_fee: public(uint256) # admin_fee * 1e10
owner: public(address)
lp_token: public(address)
redemption_price_snap: public(address)
# Token corresponding to the pool is always the last one
BASE_CACHE_EXPIRES: constant(int128) = 10 * 60 # 10 min
base_pool: public(address)
base_virtual_price: public(uint256)
base_cache_updated: public(uint256)
base_coins: public(address[BASE_N_COINS])
A_PRECISION: constant(uint256) = 100
initial_A: public(uint256)
future_A: public(uint256)
initial_A_time: public(uint256)
future_A_time: public(uint256)
admin_actions_deadline: public(uint256)
transfer_ownership_deadline: public(uint256)
future_fee: public(uint256)
future_admin_fee: public(uint256)
future_owner: public(address)
is_killed: bool
kill_deadline: uint256
KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400
@external
def __init__(
_owner: address,
_coins: address[N_COINS],
_pool_token: address,
_base_pool: address,
_redemption_price_snap: address,
_A: uint256,
_fee: uint256,
_admin_fee: uint256
):
"""
@notice Contract constructor
@param _owner Contract owner address
@param _coins Addresses of ERC20 conracts of coins
@param _pool_token Address of the token representing LP share
@param _base_pool Address of the base pool (which will have a virtual price)
@param _redemption_price_snap Address of contract providing snapshot of redemption price
@param _A Amplification coefficient multiplied by n * (n - 1)
@param _fee Fee to charge for exchanges
@param _admin_fee Admin fee
"""
for i in range(N_COINS):
assert _coins[i] != ZERO_ADDRESS
self.coins = _coins
self.initial_A = _A * A_PRECISION
self.future_A = _A * A_PRECISION
self.fee = _fee
self.admin_fee = _admin_fee
self.owner = _owner
self.kill_deadline = block.timestamp + KILL_DEADLINE_DT
self.lp_token = _pool_token
self.base_pool = _base_pool
self.base_virtual_price = Curve(_base_pool).get_virtual_price()
self.base_cache_updated = block.timestamp
self.redemption_price_snap = _redemption_price_snap
for i in range(BASE_N_COINS):
base_coin: address = Curve(_base_pool).coins(i)
self.base_coins[i] = base_coin
# approve underlying coins for infinite transfers
response: Bytes[32] = raw_call(
base_coin,
concat(
method_id("approve(address,uint256)"),
convert(_base_pool, bytes32),
convert(MAX_UINT256, bytes32),
),
max_outsize=32,
)
if len(response) > 0:
assert convert(response, bool)
@view
@internal
def _A() -> uint256:
"""
Handle ramping A up or down
"""
t1: uint256 = self.future_A_time
A1: uint256 = self.future_A
if block.timestamp < t1:
A0: uint256 = self.initial_A
t0: uint256 = self.initial_A_time
# Expressions in uint256 cannot have negative numbers, thus "if"
if A1 > A0:
return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0)
else:
return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0)
else: # when t1 == 0 or block.timestamp >= t1
return A1
@internal
@view
def sqrt(x: uint256) -> uint256:
"""
Originating from: https://github.com/vyperlang/vyper/issues/1266
"""
if x == 0:
return 0
z: uint256 = (x + 10**18) / 2
y: uint256 = x
for i in range(256):
if z == y:
return y
y = z
z = (x * 10**18 / z + z) / 2
raise "Did not converge"
@view
@external
def A() -> uint256:
return self._A() / A_PRECISION
@view
@external
def A_precise() -> uint256:
return self._A()
@view
@internal
def _get_scaled_redemption_price() -> uint256:
"""
@notice Reads a snapshot view of redemption price
@dev The fetched redemption price uses 27 decimals
@return The redemption price with appropriate scaling to match LP tokens vitual price.
"""
rate: uint256 = RedemptionPriceSnap(self.redemption_price_snap).snappedRedemptionPrice()
return rate / REDMPTION_PRICE_SCALE
@view
@internal
def _xp(_vp_rate: uint256) -> uint256[N_COINS]:
result: uint256[N_COINS] = [self._get_scaled_redemption_price(), _vp_rate]
for i in range(N_COINS):
result[i] = result[i] * self.balances[i] / PRECISION
return result
@pure
@internal
def _xp_mem(_rates: uint256[N_COINS], _balances: uint256[N_COINS]) -> uint256[N_COINS]:
result: uint256[N_COINS] = _rates
for i in range(N_COINS):
result[i] = result[i] * _balances[i] / PRECISION
return result
@internal
def _vp_rate() -> uint256:
if block.timestamp > self.base_cache_updated + BASE_CACHE_EXPIRES:
vprice: uint256 = Curve(self.base_pool).get_virtual_price()
self.base_virtual_price = vprice
self.base_cache_updated = block.timestamp
return vprice
else:
return self.base_virtual_price
@internal
@view
def _vp_rate_ro() -> uint256:
if block.timestamp > self.base_cache_updated + BASE_CACHE_EXPIRES:
return Curve(self.base_pool).get_virtual_price()
else:
return self.base_virtual_price
@pure
@internal
def _get_D(_xp: uint256[N_COINS], _amp: uint256) -> uint256:
S: uint256 = 0
Dprev: uint256 = 0
for _x in _xp:
S += _x
if S == 0:
return 0
D: uint256 = S
Ann: uint256 = _amp * N_COINS
for _i in range(255):
D_P: uint256 = D
for _x in _xp:
D_P = D_P * D / (_x * N_COINS) # If division by 0, this will be borked: only withdrawal will work. And that is good
Dprev = D
D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
# Equality with the precision of 1
if D > Dprev:
if D - Dprev <= 1:
return D
else:
if Dprev - D <= 1:
return D
# convergence typically occurs in 4 rounds or less, this should be unreachable!
# if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
raise
@view
@internal
def _get_D_mem(_rates: uint256[N_COINS], _balances: uint256[N_COINS], _amp: uint256) -> uint256:
return self._get_D(self._xp_mem(_rates, _balances), _amp)
@view
@internal
def _get_virtual_price() -> uint256:
"""
@notice The current virtual price of the pool LP token
@return LP token virtual price normalized to 1e18
"""
amp: uint256 = self._A()
vp_rate: uint256 = self._vp_rate_ro()
xp: uint256[N_COINS] = self._xp(vp_rate)
D: uint256 = self._get_D(xp, amp)
# D is in the units similar to DAI (e.g. converted to precision 1e18)
# When balanced, D = n * x_u - total virtual value of the portfolio
token_supply: uint256 = CurveToken(self.lp_token).totalSupply()
return D * PRECISION / token_supply
@view
@external
def get_virtual_price() -> uint256:
"""
@notice The current virtual price of the pool LP token
@dev Useful for calculating profits
@return LP token virtual price normalized to 1e18
"""
return self._get_virtual_price()
@view
@external
def get_virtual_price_2() -> uint256:
"""
@notice Smoother changing virtual price to accomodate for redemption price swings
@return LP token smoothed virtual price normalized to 1e18
"""
return ( self._get_virtual_price() * PRECISION ) / self.sqrt(self._get_scaled_redemption_price())
@view
@external
def calc_token_amount(_amounts: uint256[N_COINS], _is_deposit: bool) -> uint256:
"""
@notice Calculate addition or reduction in token supply from a deposit or withdrawal
@dev This calculation accounts for slippage, but not fees.
Needed to prevent front-running, not for precise calculations!
@param _amounts Amount of each coin being deposited
@param _is_deposit set True for deposits, False for withdrawals
@return Expected amount of LP tokens received
"""
amp: uint256 = self._A()
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate_ro()]
balances: uint256[N_COINS] = self.balances
D0: uint256 = self._get_D_mem(rates, balances, amp)
for i in range(N_COINS):
if _is_deposit:
balances[i] += _amounts[i]
else:
balances[i] -= _amounts[i]
D1: uint256 = self._get_D_mem(rates, balances, amp)
token_amount: uint256 = CurveToken(self.lp_token).totalSupply()
diff: uint256 = 0
if _is_deposit:
diff = D1 - D0
else:
diff = D0 - D1
return diff * token_amount / D0
@external
@nonreentrant('lock')
def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256:
"""
@notice Deposit coins into the pool
@param _amounts List of amounts of coins to deposit
@param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
@return Amount of LP tokens received by depositing
"""
assert not self.is_killed # dev: is killed
amp: uint256 = self._A()
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate_ro()]
old_balances: uint256[N_COINS] = self.balances
# Initial invariant
D0: uint256 = self._get_D_mem(rates, old_balances, amp)
lp_token: address = self.lp_token
token_supply: uint256 = CurveToken(lp_token).totalSupply()
new_balances: uint256[N_COINS] = old_balances
for i in range(N_COINS):
if token_supply == 0:
assert _amounts[i] > 0 # dev: initial deposit requires all coins
# balances store amounts of c-tokens
new_balances[i] = old_balances[i] + _amounts[i]
# Invariant after change
D1: uint256 = self._get_D_mem(rates, new_balances, amp)
assert D1 > D0
# We need to recalculate the invariant accounting for fees
# to calculate fair user's share
fees: uint256[N_COINS] = empty(uint256[N_COINS])
D2: uint256 = D1
mint_amount: uint256 = 0
if token_supply > 0:
fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
admin_fee: uint256 = self.admin_fee
# Only account for fees if we are not the first to deposit
for i in range(N_COINS):
ideal_balance: uint256 = D1 * old_balances[i] / D0
difference: uint256 = 0
if ideal_balance > new_balances[i]:
difference = ideal_balance - new_balances[i]
else:
difference = new_balances[i] - ideal_balance
fees[i] = fee * difference / FEE_DENOMINATOR
self.balances[i] = new_balances[i] - (fees[i] * admin_fee / FEE_DENOMINATOR)
new_balances[i] -= fees[i]
D2 = self._get_D_mem(rates, new_balances, amp)
mint_amount = token_supply * (D2 - D0) / D0
else:
self.balances = new_balances
mint_amount = D1 # Take the dust if there was any
assert mint_amount >= _min_mint_amount, "Slippage screwed you"
# Take coins from the sender
for i in range(N_COINS):
if _amounts[i] > 0:
# "safeTransferFrom" which works for ERC20s which return bool or not
response: Bytes[32] = raw_call(
self.coins[i],
concat(
method_id("transferFrom(address,address,uint256)"),
convert(msg.sender, bytes32),
convert(self, bytes32),
convert(_amounts[i], bytes32),
),
max_outsize=32,
)
if len(response) > 0:
assert convert(response, bool) # dev: failed transfer
# end "safeTransferFrom"
# Mint pool tokens
CurveToken(lp_token).mint(msg.sender, mint_amount)
log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount)
return mint_amount
@view
@internal
def _get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256:
"""
Calculate x[j] if one makes x[i] = x
Done by solving quadratic equation iteratively.
x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
x_1**2 + b*x_1 = c
x_1 = (x_1**2 + c) / (2*x_1 + b)
"""
# x in the input is converted to the same price/precision
assert i != j # dev: same coin
assert j >= 0 # dev: j below zero
assert j < N_COINS # dev: j above N_COINS
# should be unreachable, but good for safety
assert i >= 0
assert i < N_COINS
A: uint256 = self._A()
D: uint256 = self._get_D(_xp, A)
Ann: uint256 = A * N_COINS
c: uint256 = D
S: uint256 = 0
_x: uint256 = 0
y_prev: uint256 = 0
for _i in range(N_COINS):
if _i == i:
_x = x
elif _i != j:
_x = _xp[_i]
else:
continue
S += _x
c = c * D / (_x * N_COINS)
c = c * D * A_PRECISION / (Ann * N_COINS)
b: uint256 = S + D * A_PRECISION / Ann # - D
y: uint256 = D
for _i in range(255):
y_prev = y
y = (y*y + c) / (2 * y + b - D)
# Equality with the precision of 1
if y > y_prev:
if y - y_prev <= 1:
return y
else:
if y_prev - y <= 1:
return y
raise
@view
@external
def get_dy(i: int128, j: int128, _dx: uint256) -> uint256:
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate_ro()]
xp: uint256[N_COINS] = self._xp(rates[MAX_COIN])
x: uint256 = xp[i] + (_dx * rates[i] / PRECISION)
y: uint256 = self._get_y(i, j, x, xp)
dy: uint256 = xp[j] - y - 1
fee: uint256 = self.fee * dy / FEE_DENOMINATOR
return (dy - fee) * PRECISION / rates[j]
@view
@external
def get_dy_underlying(i: int128, j: int128, _dx: uint256) -> uint256:
# dx and dy in underlying units
vp_rate: uint256 = self._vp_rate_ro()
xp: uint256[N_COINS] = self._xp(vp_rate)
base_pool: address = self.base_pool
# Use base_i or base_j if they are >= 0
base_i: int128 = i - MAX_COIN
base_j: int128 = j - MAX_COIN
meta_i: int128 = MAX_COIN
meta_j: int128 = MAX_COIN
if base_i < 0:
meta_i = i
if base_j < 0:
meta_j = j
x: uint256 = 0
if base_i < 0:
x = xp[i] + (_dx * self._get_scaled_redemption_price() / PRECISION)
else:
if base_j < 0:
# i is from BasePool
# At first, get the amount of pool tokens
base_inputs: uint256[BASE_N_COINS] = empty(uint256[BASE_N_COINS])
base_inputs[base_i] = _dx
# Token amount transformed to underlying "dollars"
x = Curve(base_pool).calc_token_amount(base_inputs, True) * vp_rate / PRECISION
# Accounting for deposit/withdraw fees approximately
x -= x * Curve(base_pool).fee() / (2 * FEE_DENOMINATOR)
# Adding number of pool tokens
x += xp[MAX_COIN]
else:
# If both are from the base pool
return Curve(base_pool).get_dy(base_i, base_j, _dx)
# This pool is involved only when in-pool assets are used
y: uint256 = self._get_y(meta_i, meta_j, x, xp)
dy: uint256 = xp[meta_j] - y - 1
dy = (dy - self.fee * dy / FEE_DENOMINATOR)
if j == REDEMPTION_COIN:
dy = (dy * PRECISION) / self._get_scaled_redemption_price()
# If output is going via the metapool
if base_j >= 0:
# j is from BasePool
# The fee is already accounted for
dy = Curve(base_pool).calc_withdraw_one_coin(dy * PRECISION / vp_rate, base_j)
return dy
@external
@nonreentrant('lock')
def exchange(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256:
"""
@notice Perform an exchange between two coins
@dev Index values can be found via the `coins` public getter method
@param i Index value for the coin to send
@param j Index valie of the coin to recieve
@param _dx Amount of `i` being exchanged
@param _min_dy Minimum amount of `j` to receive
@return Actual amount of `j` received
"""
assert not self.is_killed # dev: is killed
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate()]
old_balances: uint256[N_COINS] = self.balances
xp: uint256[N_COINS] = self._xp_mem(rates, old_balances)
x: uint256 = xp[i] + _dx * rates[i] / PRECISION
y: uint256 = self._get_y(i, j, x, xp)
dy: uint256 = xp[j] - y - 1 # -1 just in case there were some rounding errors
dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
# Convert all to real units
dy = (dy - dy_fee) * PRECISION / rates[j]
assert dy >= _min_dy, "Too few coins in result"
dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR
dy_admin_fee = dy_admin_fee * PRECISION / rates[j]
# Change balances exactly in same way as we change actual ERC20 coin amounts
self.balances[i] = old_balances[i] + _dx
# When rounding errors happen, we undercharge admin fee in favor of LP
self.balances[j] = old_balances[j] - dy - dy_admin_fee
response: Bytes[32] = raw_call(
self.coins[i],
concat(
method_id("transferFrom(address,address,uint256)"),
convert(msg.sender, bytes32),
convert(self, bytes32),
convert(_dx, bytes32),
),
max_outsize=32,
)
if len(response) > 0:
assert convert(response, bool)
response = raw_call(
self.coins[j],
concat(
method_id("transfer(address,uint256)"),
convert(msg.sender, bytes32),
convert(dy, bytes32),
),
max_outsize=32,
)
if len(response) > 0:
assert convert(response, bool)
log TokenExchange(msg.sender, i, _dx, j, dy)
return dy
@external
@nonreentrant('lock')
def exchange_underlying(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256:
"""
@notice Perform an exchange between two underlying coins
@dev Index values can be found via the `underlying_coins` public getter method
@param i Index value for the underlying coin to send
@param j Index value of the underlying coin to receive
@param _dx Amount of `i` being exchanged
@param _min_dy Minimum amount of `j` to receive
@return Actual amount of `j` received
"""
assert not self.is_killed # dev: is killed
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate()]
base_pool: address = self.base_pool
# Use base_i or base_j if they are >= 0
base_i: int128 = i - MAX_COIN
base_j: int128 = j - MAX_COIN
meta_i: int128 = MAX_COIN
meta_j: int128 = MAX_COIN
if base_i < 0:
meta_i = i
if base_j < 0:
meta_j = j
dy: uint256 = 0
# Addresses for input and output coins
input_coin: address = ZERO_ADDRESS
output_coin: address = ZERO_ADDRESS
if base_i < 0:
input_coin = self.coins[i]
else:
input_coin = self.base_coins[base_i]
if base_j < 0:
output_coin = self.coins[j]
else:
output_coin = self.base_coins[base_j]
# Handle potential Tether fees
dx_w_fee: uint256 = _dx
if input_coin == FEE_ASSET:
dx_w_fee = ERC20(FEE_ASSET).balanceOf(self)
response: Bytes[32] = raw_call(
input_coin,
concat(
method_id("transferFrom(address,address,uint256)"),
convert(msg.sender, bytes32),
convert(self, bytes32),
convert(_dx, bytes32),
),
max_outsize=32,
)
if len(response) > 0:
assert convert(response, bool)
# Handle potential Tether fees
if input_coin == FEE_ASSET:
dx_w_fee = ERC20(FEE_ASSET).balanceOf(self) - dx_w_fee
if base_i < 0 or base_j < 0:
old_balances: uint256[N_COINS] = self.balances
xp: uint256[N_COINS] = self._xp_mem(rates, old_balances)
x: uint256 = 0
if base_i < 0:
x = xp[i] + dx_w_fee * rates[i] / PRECISION
else:
# i is from BasePool
# At first, get the amount of pool tokens
base_inputs: uint256[BASE_N_COINS] = empty(uint256[BASE_N_COINS])
base_inputs[base_i] = dx_w_fee
coin_i: address = self.coins[MAX_COIN]
# Deposit and measure delta
x = ERC20(coin_i).balanceOf(self)
Curve(base_pool).add_liquidity(base_inputs, 0)
# Need to convert pool token to "virtual" units using rates
# dx is also different now
dx_w_fee = ERC20(coin_i).balanceOf(self) - x
x = dx_w_fee * rates[MAX_COIN] / PRECISION
# Adding number of pool tokens
x += xp[MAX_COIN]
y: uint256 = self._get_y(meta_i, meta_j, x, xp)
# Either a real coin or token
dy = xp[meta_j] - y - 1 # -1 just in case there were some rounding errors
dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
# Convert all to real units
# Works for both pool coins and real coins
dy = (dy - dy_fee) * PRECISION / rates[meta_j]
dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR
dy_admin_fee = dy_admin_fee * PRECISION / rates[meta_j]
# Change balances exactly in same way as we change actual ERC20 coin amounts
self.balances[meta_i] = old_balances[meta_i] + dx_w_fee
# When rounding errors happen, we undercharge admin fee in favor of LP
self.balances[meta_j] = old_balances[meta_j] - dy - dy_admin_fee
# Withdraw from the base pool if needed
if base_j >= 0:
out_amount: uint256 = ERC20(output_coin).balanceOf(self)
Curve(base_pool).remove_liquidity_one_coin(dy, base_j, 0)
dy = ERC20(output_coin).balanceOf(self) - out_amount
assert dy >= _min_dy, "Too few coins in result"
else:
# If both are from the base pool
dy = ERC20(output_coin).balanceOf(self)
Curve(base_pool).exchange(base_i, base_j, dx_w_fee, _min_dy)
dy = ERC20(output_coin).balanceOf(self) - dy
# "safeTransfer" which works for ERC20s which return bool or not
response = raw_call(
output_coin,
concat(
method_id("transfer(address,uint256)"),
convert(msg.sender, bytes32),
convert(dy, bytes32),
),
max_outsize=32,
) # dev: failed transfer
if len(response) > 0:
assert convert(response, bool) # dev: failed transfer
# end "safeTransfer"
log TokenExchangeUnderlying(msg.sender, i, _dx, j, dy)
return dy
@external
@nonreentrant('lock')
def remove_liquidity(_amount: uint256, _min_amounts: uint256[N_COINS]) -> uint256[N_COINS]:
"""
@notice Withdraw coins from the pool
@dev Withdrawal amounts are based on current deposit ratios
@param _amount Quantity of LP tokens to burn in the withdrawal
@param _min_amounts Minimum amounts of underlying coins to receive
@return List of amounts of coins that were withdrawn
"""
lp_token: address = self.lp_token
total_supply: uint256 = CurveToken(lp_token).totalSupply()
amounts: uint256[N_COINS] = empty(uint256[N_COINS])
for i in range(N_COINS):
old_balance: uint256 = self.balances[i]
value: uint256 = old_balance * _amount / total_supply
assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
self.balances[i] = old_balance - value
amounts[i] = value
ERC20(self.coins[i]).transfer(msg.sender, value)
CurveToken(lp_token).burnFrom(msg.sender, _amount) # dev: insufficient funds
log RemoveLiquidity(msg.sender, amounts, empty(uint256[N_COINS]), total_supply - _amount)
return amounts
@external
@nonreentrant('lock')
def remove_liquidity_imbalance(_amounts: uint256[N_COINS], _max_burn_amount: uint256) -> uint256:
"""
@notice Withdraw coins from the pool in an imbalanced amount
@param _amounts List of amounts of underlying coins to withdraw
@param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
@return Actual amount of the LP token burned in the withdrawal
"""
assert not self.is_killed # dev: is killed
amp: uint256 = self._A()
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), self._vp_rate_ro()]
old_balances: uint256[N_COINS] = self.balances
new_balances: uint256[N_COINS] = old_balances
D0: uint256 = self._get_D_mem(rates, old_balances, amp)
for i in range(N_COINS):
new_balances[i] -= _amounts[i]
D1: uint256 = self._get_D_mem(rates, new_balances, amp)
fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
admin_fee: uint256 = self.admin_fee
fees: uint256[N_COINS] = empty(uint256[N_COINS])
for i in range(N_COINS):
ideal_balance: uint256 = D1 * old_balances[i] / D0
difference: uint256 = 0
if ideal_balance > new_balances[i]:
difference = ideal_balance - new_balances[i]
else:
difference = new_balances[i] - ideal_balance
fees[i] = fee * difference / FEE_DENOMINATOR
self.balances[i] = new_balances[i] - (fees[i] * admin_fee / FEE_DENOMINATOR)
new_balances[i] -= fees[i]
D2: uint256 = self._get_D_mem(rates, new_balances, amp)
lp_token: address = self.lp_token
token_supply: uint256 = CurveToken(lp_token).totalSupply()
token_amount: uint256 = (D0 - D2) * token_supply / D0
assert token_amount != 0 # dev: zero tokens burned
token_amount += 1 # In case of rounding errors - make it unfavorable for the "attacker"
assert token_amount <= _max_burn_amount, "Slippage screwed you"
CurveToken(lp_token).burnFrom(msg.sender, token_amount) # dev: insufficient funds
for i in range(N_COINS):
if _amounts[i] != 0:
ERC20(self.coins[i]).transfer(msg.sender, _amounts[i])
log RemoveLiquidityImbalance(msg.sender, _amounts, fees, D1, token_supply - token_amount)
return token_amount
@pure
@internal
def _get_y_D(A: uint256, i: int128, _xp: uint256[N_COINS], D: uint256) -> uint256:
"""
Calculate x[i] if one reduces D from being calculated for xp to D
Done by solving quadratic equation iteratively.
x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
x_1**2 + b*x_1 = c
x_1 = (x_1**2 + c) / (2*x_1 + b)
"""
# x in the input is converted to the same price/precision
assert i >= 0 # dev: i below zero
assert i < N_COINS # dev: i above N_COINS
Ann: uint256 = A * N_COINS
c: uint256 = D
S: uint256 = 0
_x: uint256 = 0
y_prev: uint256 = 0
for _i in range(N_COINS):
if _i != i:
_x = _xp[_i]
else:
continue
S += _x
c = c * D / (_x * N_COINS)
c = c * D * A_PRECISION / (Ann * N_COINS)
b: uint256 = S + D * A_PRECISION / Ann
y: uint256 = D
for _i in range(255):
y_prev = y
y = (y*y + c) / (2 * y + b - D)
# Equality with the precision of 1
if y > y_prev:
if y - y_prev <= 1:
return y
else:
if y_prev - y <= 1:
return y
raise
@view
@internal
def _calc_withdraw_one_coin(_token_amount: uint256, i: int128, _vp_rate: uint256) -> (uint256, uint256, uint256):
# First, need to calculate
# * Get current D
# * Solve Eqn against y_i for D - _token_amount
amp: uint256 = self._A()
xp: uint256[N_COINS] = self._xp(_vp_rate)
D0: uint256 = self._get_D(xp, amp)
total_supply: uint256 = CurveToken(self.lp_token).totalSupply()
D1: uint256 = D0 - _token_amount * D0 / total_supply
new_y: uint256 = self._get_y_D(amp, i, xp, D1)
fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
rates: uint256[N_COINS] = [self._get_scaled_redemption_price(), _vp_rate]
xp_reduced: uint256[N_COINS] = xp
dy_0: uint256 = (xp[i] - new_y) * PRECISION / rates[i] # w/o fees
for j in range(N_COINS):
dx_expected: uint256 = 0
if j == i:
dx_expected = xp[j] * D1 / D0 - new_y
else:
dx_expected = xp[j] - xp[j] * D1 / D0
xp_reduced[j] -= fee * dx_expected / FEE_DENOMINATOR
dy: uint256 = xp_reduced[i] - self._get_y_D(amp, i, xp_reduced, D1)
dy = (dy - 1) * PRECISION / rates[i] # Withdraw less to account for rounding errors
return dy, dy_0 - dy, total_supply
@view
@external
def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256:
"""
@notice Calculate the amount received when withdrawing a single coin
@param _token_amount Amount of LP tokens to burn in the withdrawal
@param i Index value of the coin to withdraw
@return Amount of coin received
"""
vp_rate: uint256 = self._vp_rate_ro()
return self._calc_withdraw_one_coin(_token_amount, i, vp_rate)[0]
@external
@nonreentrant('lock')
def remove_liquidity_one_coin(_token_amount: uint256, i: int128, _min_amount: uint256) -> uint256:
"""
@notice Withdraw a single coin from the pool
@param _token_amount Amount of LP tokens to burn in the withdrawal
@param i Index value of the coin to withdraw
@param _min_amount Minimum amount of coin to receive
@return Amount of coin received
"""
assert not self.is_killed # dev: is killed
vp_rate: uint256 = self._vp_rate()
dy: uint256 = 0
dy_fee: uint256 = 0
total_supply: uint256 = 0
dy, dy_fee, total_supply = self._calc_withdraw_one_coin(_token_amount, i, vp_rate)
assert dy >= _min_amount, "Not enough coins removed"
self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR)
CurveToken(self.lp_token).burnFrom(msg.sender, _token_amount) # dev: insufficient funds
ERC20(self.coins[i]).transfer(msg.sender, dy)
log RemoveLiquidityOne(msg.sender, _token_amount, dy, total_supply - _token_amount)
return dy
### Admin functions ###
@external
def ramp_A(_future_A: uint256, _future_time: uint256):
assert msg.sender == self.owner # dev: only owner
assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
assert _future_time >= block.timestamp + MIN_RAMP_TIME # dev: insufficient time
initial_A: uint256 = self._A()
future_A_p: uint256 = _future_A * A_PRECISION
assert _future_A > 0 and _future_A < MAX_A
if future_A_p < initial_A:
assert future_A_p * MAX_A_CHANGE >= initial_A
else:
assert future_A_p <= initial_A * MAX_A_CHANGE
self.initial_A = initial_A
self.future_A = future_A_p
self.initial_A_time = block.timestamp
self.future_A_time = _future_time
log RampA(initial_A, future_A_p, block.timestamp, _future_time)
@external
def stop_ramp_A():
assert msg.sender == self.owner # dev: only owner
current_A: uint256 = self._A()
self.initial_A = current_A
self.future_A = current_A
self.initial_A_time = block.timestamp
self.future_A_time = block.timestamp
# now (block.timestamp < t1) is always False, so we return saved A
log StopRampA(current_A, block.timestamp)
@external
def commit_new_fee(_new_fee: uint256, _new_admin_fee: uint256):
assert msg.sender == self.owner # dev: only owner
assert self.admin_actions_deadline == 0 # dev: active action
assert _new_fee <= MAX_FEE # dev: fee exceeds maximum
assert _new_admin_fee <= MAX_ADMIN_FEE # dev: admin fee exceeds maximum
deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
self.admin_actions_deadline = deadline
self.future_fee = _new_fee
self.future_admin_fee = _new_admin_fee
log CommitNewFee(deadline, _new_fee, _new_admin_fee)
@external
def apply_new_fee():
assert msg.sender == self.owner # dev: only owner
assert block.timestamp >= self.admin_actions_deadline # dev: insufficient time
assert self.admin_actions_deadline != 0 # dev: no active action
self.admin_actions_deadline = 0
fee: uint256 = self.future_fee
admin_fee: uint256 = self.future_admin_fee
self.fee = fee
self.admin_fee = admin_fee
log NewFee(fee, admin_fee)
@external
def revert_new_parameters():
assert msg.sender == self.owner # dev: only owner
self.admin_actions_deadline = 0
@external
def commit_transfer_ownership(_owner: address):
assert msg.sender == self.owner # dev: only owner
assert self.transfer_ownership_deadline == 0 # dev: active transfer
deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
self.transfer_ownership_deadline = deadline
self.future_owner = _owner
log CommitNewAdmin(deadline, _owner)
@external
def apply_transfer_ownership():
assert msg.sender == self.owner # dev: only owner
assert block.timestamp >= self.transfer_ownership_deadline # dev: insufficient time
assert self.transfer_ownership_deadline != 0 # dev: no active transfer
self.transfer_ownership_deadline = 0
owner: address = self.future_owner
self.owner = owner
log NewAdmin(owner)
@external
def revert_transfer_ownership():
assert msg.sender == self.owner # dev: only owner
self.transfer_ownership_deadline = 0
@view
@external
def admin_balances(i: uint256) -> uint256:
return ERC20(self.coins[i]).balanceOf(self) - self.balances[i]
@external
def withdraw_admin_fees():
assert msg.sender == self.owner # dev: only owner
for i in range(N_COINS):
coin: address = self.coins[i]
value: uint256 = ERC20(coin).balanceOf(self) - self.balances[i]
if value > 0:
ERC20(coin).transfer(msg.sender, value)
@external
def kill_me():
assert msg.sender == self.owner # dev: only owner
assert self.kill_deadline > block.timestamp # dev: deadline has passed
self.is_killed = True
@external
def unkill_me():
assert msg.sender == self.owner # dev: only owner
self.is_killed = False
Read Contract
A 0xf446c1d0 → uint256
A_precise 0x76a2f0f0 → uint256
admin_actions_deadline 0x405e28f8 → uint256
admin_balances 0xe2e7d264 → uint256
admin_fee 0xfee3f7f9 → uint256
balances 0x4903b0d1 → uint256
base_cache_updated 0x8296f84f → uint256
base_coins 0x87cb4f57 → address
base_pool 0x5d6362bb → address
base_virtual_price 0x95ccc02f → uint256
calc_token_amount 0xed8e84f3 → uint256
calc_withdraw_one_coin 0xcc2b27d7 → uint256
coins 0xc6610657 → address
fee 0xddca3f43 → uint256
future_A 0xb4b577ad → uint256
future_A_time 0x14052288 → uint256
future_admin_fee 0xe3824462 → uint256
future_fee 0x58680d0b → uint256
future_owner 0x1ec0cdc1 → address
get_dy 0x5e0d443f → uint256
get_dy_underlying 0x07211ef7 → uint256
get_virtual_price 0xbb7b8b80 → uint256
get_virtual_price_2 0x5ae7a3fe → uint256
initial_A 0x5409491a → uint256
initial_A_time 0x2081066c → uint256
lp_token 0x82c63066 → address
owner 0x8da5cb5b → address
redemption_price_snap 0x85cab755 → address
transfer_ownership_deadline 0xe0a0b586 → uint256
Write Contract 17 functions
These functions modify contract state and require a wallet transaction to execute.
add_liquidity 0x0b4c7e4d
uint256[2] _amounts
uint256 _min_mint_amount
returns: uint256
apply_new_fee 0x4f12fe97
No parameters
apply_transfer_ownership 0x6a1c05ae
No parameters
commit_new_fee 0x5b5a1467
uint256 _new_fee
uint256 _new_admin_fee
commit_transfer_ownership 0x6b441a40
address _owner
exchange 0x3df02124
int128 i
int128 j
uint256 _dx
uint256 _min_dy
returns: uint256
exchange_underlying 0xa6417ed6
int128 i
int128 j
uint256 _dx
uint256 _min_dy
returns: uint256
kill_me 0xe3698853
No parameters
ramp_A 0x3c157e64
uint256 _future_A
uint256 _future_time
remove_liquidity 0x5b36389c
uint256 _amount
uint256[2] _min_amounts
returns: uint256[2]
remove_liquidity_imbalance 0xe3103273
uint256[2] _amounts
uint256 _max_burn_amount
returns: uint256
remove_liquidity_one_coin 0x1a4d01d2
uint256 _token_amount
int128 i
uint256 _min_amount
returns: uint256
revert_new_parameters 0x226840fb
No parameters
revert_transfer_ownership 0x86fbf193
No parameters
stop_ramp_A 0x551a6588
No parameters
unkill_me 0x3046f972
No parameters
withdraw_admin_fees 0x30c54085
No parameters
Token Balances (1)
View Transfers →Recent Transactions
This address has 1 on-chain transactions, but only 0.8% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →