Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x3C91EAeac42DfaEad5F356167c52837e443b9f94
Balance 0 ETH
Nonce 1
Code Size 23635 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23635 bytes
0x5f3560e01c60056005600683060261522a01601b395f51600760078260ff16848460181c0260181c06028260081c61ffff1601601939505f51818160181c14600336111661004c5761294e565b8060fe16361034826001160217615226578060081c61ffff16565b60206153f360403960206040f35b602060043560206154535f395f518110156152265760051b6080016153f30160403960206040f35b600a5460405260206040f35b600b5460405260206040f35b64012a05f20060405260206040f35b600c5460405260206040f35b600d5460405260206040f35b600e5460405260206040f35b600f5460405260206040f35b600435601054811015615226576011015460405260206040f35b60235460405260206040f35b60245460405260206040f35b60255460405260206040f35b6020806040528060400160206020615b335f395f510180615b338339508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b6020806040528060400160206020615b935f395f510180615b938339508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b601260405260206040f35b60208060805260066040527f76372e302e30000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b6004358060a01c6152265760405260266040516020525f5260405f205460605260206060f35b6004358060a01c615226576040526024358060a01c6152265760605260276040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6004358060a01c6152265760405260296040516020525f5260405f205460605260206060f35b6020615c1360403960206040f35b33611360526102ee565b6084358060a01c61522657611360525b60043580600f0b8118615226576113205260243580600f0b811861522657611340525f546002146152265760025f55602033610dc05261132051610de05261134051610e005260406044610e203761136051610e60525f610e8052610354611380614466565b61138060035f55f35b3361136052610377565b6084358060a01c61522657611360525b60043580600f0b8118615226576113205260243580600f0b811861522657611340525f546002146152265760025f5560206156935f395f5161522657602033610dc05261132051610de05261134051610e005260406044610e203761136051610e60526001610e80526103eb611380614466565b61138060035f55f35b33610a805261040e565b6044358060a01c61522657610a80525b600435600401600881351161522657803560208160051b018083610960375050505f546002146152265760025f55610a80511561522657610450610ac061323c565b610ac051610aa052610463610be0613055565b610be0805160208160051b0180610ac0828560045afa50505050610488610d00612d26565b610d00805160208160051b0180610be0828560045afa50505050610be05160208160051b01806103c082610be060045afa505050610ac05160208160051b01806104e082610ac060045afa505050610aa051610600526104e9610d20614714565b610d2051610d0052602854610d2052610ac05160208160051b0180610d4082610ac060045afa5050505f60206154135f395f51600881116152265780156105c857905b80610e6052610e6051610960518110156152265760051b6109800151156105b357610e6051610d40518110156152265760051b610d60018051610e6051604052610e6051610960518110156152265760051b6109800151606052336080525f60a052610599610e80612993565b610e805180820182811061522657905090508152506105bd565b610d205115615226575b60010181811861052c575b5050610be05160208160051b01806103c082610be060045afa505050610d405160208160051b01806104e082610d4060045afa505050610aa05161060052610611610e80614714565b610e8051610e6052610d0051610e60511115615226575f610e80525f610fa052610d2051156109a857606036610fc03760206153f35f395f51610d0051610e60518082018281106152265790509050046110205260403661104037600160206153f35f395f510360021b60206153f35f395f51600a540204611080525f60206154135f395f51600881116152265780156108ad57905b806110a052610e60516110a051610ac0518110156152265760051b610ae001518082028115838383041417156152265790509050610d005180156152265780820490509050610fc0525f610fe0526110a051610d40518110156152265760051b610d6001516110005261100051610fc0511161072f57610fc0516110005103610fe05261073d565b61100051610fc05103610fe0525b670de0b6b3a76400006110a051610be0518110156152265760051b610c0001516110a051610ac0518110156152265760051b610ae00151611000518082018281106152265790509050808202811583838304141715615226579050905004611040526110405160405261102051606052611080516080526107bf6110c06138c4565b6110c05161106052610e805160078111615226576402540be40061106051610fe0518082028115838383041417156152265790509050048160051b610ea0015260018101610e8052506110a0516010548110156152265760110180546402540be4006110a051610e80518110156152265760051b610ea0015164012a05f20081028164012a05f2008204186152265790500480820182811061522657905090508155506110a051610d40518110156152265760051b610d600180516110a051610e80518110156152265760051b610ea0015180820382811161522657905090508152506001018181186106a7575b5050610be05160208160051b0180604082610be060045afa505050610d405160208160051b018061016082610d4060045afa5050506108ed6111c0613188565b6111c0805160208160051b01806110a0828560045afa505050506110a05160208160051b01806040826110a060045afa505050610aa051610160526109336111c0613347565b6111c051610e6052610d0051610d2051610e6051610d00518082038281116152265790509050808202811583838304141715615226579050905004610fa0526110a05160208160051b0180610340826110a060045afa505050610aa05161046052610e605161048052610a23613ed056610a23565b610e6051610fa052610e6051604052610e60516060526109c9610fc0612952565b610fc0516022556025546040526109e1611000613984565b6110006040610fc060408360045afa505042610fe0511015610a235742610fe052610fc051604052610fe051606052610a1b611000612952565b611000516025555b602435610fa0511015610a95576014610fc0527f536c697070616765207363726577656420796f75000000000000000000000000610fe052610fc050610fc05180610fe001601f825f031636823750506308c379a0610f80526020610fa052601f19601f610fc0510116604401610f9cfd5b610d2051610fa0518082018281106152265790509050610d20526026610a80516020525f5260405f208054610fa0518082018281106152265790509050815550610d2051602855610a80515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef610fa051610fc0526020610fc0a3337f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a2608080610fc05280610fc0015f610960518083528060051b5f8260088111615226578015610b7a57905b8060051b61098001518160051b602088010152600101818118610b5c575b5050820160200191505090508101905080610fe05280610fc0015f610e80518083528060051b5f8260088111615226578015610bd057905b8060051b610ea001518160051b602088010152600101818118610bb2575b50508201602001915050905081019050610e605161100052610d205161102052610fc0a26020610fa060035f55f35b33610b0052610c19565b6064358060a01c61522657610b00525b60243580600f0b811861522657610ae0525f546002146152265760025f556004351561522657606036610b20375f610c80525f610ca0526004356103c052610ae0516103e052610c6a610cc0614a57565b610cc08051610b20526020810151610b405260408101805160208160051b0180610b60828560045afa50505050610160810151610c8052610180810151610ca05250604435610b20511015610d1e576018610cc0527f4e6f7420656e6f75676820636f696e732072656d6f7665640000000000000000610ce052610cc050610cc05180610ce001601f825f031636823750506308c379a0610c80526020610ca052601f19601f610cc0510116604401610c9cfd5b610ae0516010548110156152265760110180546402540be400610b405164012a05f20081028164012a05f20082041861522657905004808201828110615226579050905081555033604052600435606052610d77614f3d565b610ae051604052610b2051606052610b0051608052610d94612b55565b337f6f48129db1f37ccb9cc5dd7e119cb32750cabdf75b48375d730d26ce3659bbe1610ae051610cc052600435610ce052610b2051610d0052602854610d20526080610cc0a2610b605160208160051b018061034082610b6060045afa505050610c805161046052610ca05161048052610e0c613ed0565b6020610b2060035f55f35b33610a8052610e31565b6044358060a01c61522657610a80525b600435600401600881351161522657803560208160051b018083610960375050505f546002146152265760025f55610e6a610ac061323c565b610ac051610aa052610e7d610be0612d26565b610be0805160208160051b0180610ac0828560045afa50505050610ea2610d00613055565b610d00805160208160051b0180610be0828560045afa50505050610ac05160208160051b01806103c082610ac060045afa505050610be05160208160051b01806104e082610be060045afa505050610aa05161060052610f03610d20614714565b610d2051610d0052610be05160208160051b0180610d2082610be060045afa5050505f60206154135f395f5160088111615226578015610fdb57905b80610e4052610e4051610960518110156152265760051b610980015115610fd057610e4051610d20518110156152265760051b610d40018051610e4051610960518110156152265760051b61098001518082038281116152265790509050815250610e4051604052610e4051610960518110156152265760051b6109800151606052610a8051608052610fd0612b55565b600101818118610f3f575b5050610ac05160208160051b01806103c082610ac060045afa505050610d205160208160051b01806104e082610d2060045afa505050610aa05161060052611024610e60614714565b610e6051610e4052600160206153f35f395f510360021b60206153f35f395f51600a540204610e605260206153f35f395f51610d0051610e4051808201828110615226579050905004610e80525f610ea0525f610fc052608036610fe0375f60206154135f395f51600881116152265780156112a257905b8061106052610e405161106051610be0518110156152265760051b610c0001518082028115838383041417156152265790509050610d005180156152265780820490509050611000525f6110205261106051610d20518110156152265760051b610d400151611040526110405161100051116111245761100051611040510361102052611132565b611040516110005103611020525b670de0b6b3a764000061106051610ac0518110156152265760051b610ae0015161106051610be0518110156152265760051b610c000151611040518082018281106152265790509050808202811583838304141715615226579050905004610fe052610fe051604052610e8051606052610e60516080526111b46110806138c4565b61108051610fc052610ea05160078111615226576402540be400610fc051611020518082028115838383041417156152265790509050048160051b610ec0015260018101610ea05250611060516010548110156152265760110180546402540be40061106051610ea0518110156152265760051b610ec0015164012a05f20081028164012a05f20082041861522657905004808201828110615226579050905081555061106051610d20518110156152265760051b610d4001805161106051610ea0518110156152265760051b610ec00151808203828111615226579050905081525060010181811861109c575b5050610ac05160208160051b01806103c082610ac060045afa505050610d205160208160051b01806104e082610d2060045afa505050610aa051610600526112eb611060614714565b61106051610e4052610ac05160208160051b0180604082610ac060045afa505050610d205160208160051b018061016082610d2060045afa505050611331611060613188565b611060805160208160051b0180611180828560045afa50505050610aa0516112a052610e40516112c05261016061034061016061118060045afa50611374613ed0565b60285461106052610d0051610d0051610e40518082038281116152265790509050611060518082028115838383041417156152265790509050046001810181811061522657905061108052600261108051106152265760243561108051111561143c5760146110a0527f536c697070616765207363726577656420796f750000000000000000000000006110c0526110a0506110a051806110c001601f825f031636823750506308c379a061106052602061108052601f19601f6110a051011660440161107cfd5b336040526110805160605261144f614f3d565b337f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af17556080806110a052806110a0015f610960518083528060051b5f82600881116152265780156114b957905b8060051b61098001518160051b60208801015260010181811861149b575b50508201602001915050905081019050806110c052806110a0015f610ea0518083528060051b5f826008811161522657801561150f57905b8060051b610ec001518160051b6020880101526001018181186114f1575b50508201602001915050905081019050610e40516110e05261106051611080518082038281116152265790509050611100526110a0a2602061108060035f55f35b336103c05260016103e052611599565b6044358060a01c615226576103c05260016103e052611599565b6044358060a01c615226576103c0526064358060011c615226576103e0525b602435600401600881351161522657803560208160051b0180836102a0375050505f546002146152265760025f5560285461040052600435156152265760206153f35f395f516102a05118615226575f610420526115f8610660613055565b610660805160208160051b0180610540828560045afa505050505f610660525f60206154135f395f516008811161522657801561176257905b80610680526104005161068051610540518110156152265760051b610560015160043580820281158383830414171561522657905090500461066052610680516102a0518110156152265760051b6102c001516106605110156117185760306106a0527f5769746864726177616c20726573756c74656420696e20666577657220636f696106c0527f6e73207468616e206578706563746564000000000000000000000000000000006106e0526106a0506106a051806106c001601f825f031636823750506308c379a061066052602061068052601f19601f6106a051011660440161067cfd5b610420516007811161522657610660518160051b610440015260018101610420525061068051604052610660516060526103c051608052611757612b55565b600101818118611631575b505033604052600435606052611776614f3d565b6025546040526117876106c0613984565b6106c0604061068060408360045afa50506022546106c0526fffffffffffffffffffffffffffffffff6106c051166106e0526106e051610400516106e0516004358082028115838383041417156152265790509050048082038281116152265790509050610740526106c05161012052602454610140526106a05161016052611811610700613dc6565b610700516107605260406040604061074060045afa50611832610720612952565b61072051602255426106a051101561186a57426106a052610680516040526106a051606052611862610700612952565b610700516025555b337f347ad828e58cbe534d8f6b67985d791360756b18f0d95fd9f197a66cc46480ea6060806107005280610700015f610420518083528060051b5f82600881116152265780156118d457905b8060051b61044001518160051b6020880101526001018181186118b6575b50508201602001915050905081019050806107205280610700015f5f82525f5f5f6008811161522657801561191b57905b5f8160051b602087010152600101818118611905575b505081016020019050905081019050600435610400510361074052610700a26103e0511561194b5761194b614faa565b6020806107005280610700015f610420518083528060051b5f826008811161522657801561199357905b8060051b61044001518160051b602088010152600101818118611975575b5050820160200191505090508101905061070060035f55f35b5f546002146152265760025f556119c1614faa565b60035f55005b6fffffffffffffffffffffffffffffffff60043560195481101561522657601a01541660405260206040f35b60043560195481101561522657601a015460801c60405260206040f35b611a1b6103e061323c565b6103e0516103c052611a2e610500612d26565b610500805160208160051b0180610860828560045afa50505050611a53610620613055565b610620805160208160051b0180610980828560045afa50505050610240604061024061086060045afa50611a88610740613188565b610740805160208160051b01806103e0828560045afa505050506103e05160208160051b01806040826103e060045afa5050506103c05161016052611ace610520613347565b610520516105005260206103e05160208160051b01806040826103e060045afa5050506103c051610160526105005161018052611b0c6105206139a9565b61052060043581518110156152265760051b60208201019050f35b5f5460021461522657602060043560195481101561522657601a015461012052602354610140526fffffffffffffffffffffffffffffffff6025541661016052611b72610200613dc6565b610200f35b5f54600214615226576020602254610120526024546101405260255460801c61016052611ba5610200613dc6565b610200f35b6004358060a01c6152265760c0523360405260c051606052602435608052611bd06151aa565b600160e052602060e0f35b6004358060a01c6152265760c0526024358060a01c6152265760e05260c05160405260e051606052604435608052611c116151aa565b602760c0516020525f5260405f2080336020525f5260405f20905054610100527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101005114611cc8576101005160443580820382811161522657905090506101205261012051602760c0516020525f5260405f2080336020525f5260405f209050553360c0517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92561012051610140526020610140a35b6001610120526020610120f35b6004358060a01c615226576040526024356027336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560243560605260206060a3600160605260206060f35b6004358060a01c61522657610120526024358060a01c61522657610140526084358060081c61522657610160526101205115615226576064354211615226576029610120516020525f5260405f2054610180525f60026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c0805160208201836103200181518152505080830192505050611de2610200615117565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120513b15611fab575f604060a46102603760406102405261024080516020820183610320018281848460045afa50505080830192505050610160516102a0526102a0601f810180516102e0525060016102c0526102c09050805160208201836103200181518152505080830192505050806103005261030090506020815101806101c0828460045afa5050507f1626ba7e0000000000000000000000000000000000000000000000000000000061012051631626ba7e6102405260406101a051610260528061028052806102600160206101c051018082826101c060045afa50508051806020830101601f825f03163682375050601f19601f82516020010116905081015050602061024060c461025c845afa611f93573d5f5f3e3d5ffd5b60203d10615226576102409050511861522657611feb565b610120515f610240526101a0516101c052610160516101e05260a4356102005260c43561022052602061024060806101c060015afa506102405118615226575b6044356027610120516020525f5260405f2080610140516020525f5260405f20905055600161018051016029610120516020525f5260405f205561014051610120517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256044356101c05260206101c0a360016101c05260206101c0f35b6020612075610120615117565b610120f35b60043580600f0b81186152265760405260243580600f0b811861522657606052602060206154335f395f5163e31593d8608052602060806004609c845afa6120c4573d5f5f3e3d5ffd5b60203d10615226576080518060a01c6152265760c05260c09050516383aa796a60e0526040516101005260605161012052604435610140523061016052602060e0608460fc845afa612118573d5f5f3e3d5ffd5b60203d106152265760e09050f35b60043580600f0b81186152265760405260243580600f0b811861522657606052602060206154335f395f5163e31593d8608052602060806004609c845afa612170573d5f5f3e3d5ffd5b60203d10615226576080518060a01c6152265760c05260c0905051630c601c2c60e0526040516101005260605161012052604435610140523061016052602060e0608460fc845afa6121c4573d5f5f3e3d5ffd5b60203d106152265760e09050f35b60243580600f0b811861522657610ae05260206004356103c052610ae0516103e0526121ff610b00614a57565b610b00f35b5f546002146152265760285460405260206040f35b5f546002146152265761222d6103e061323c565b6103e0516103c052612240610500612d26565b610500805160208160051b0180610860828560045afa50505050612265610620613055565b610620805160208160051b0180610980828560045afa50505050610240604061024061086060045afa5061229a610740613188565b610740805160208160051b01806103e0828560045afa505050506103e05160208160051b01806040826103e060045afa5050506103c051610160526122e0610520613347565b610520516105005261050051670de0b6b3a7640000810281670de0b6b3a764000082041861522657905060285480156152265780820490509050610520526020610520f35b600435600401600881351161522657803560208160051b0180836040375050506024358060011c6152265761016052602060206154335f395f5163e31593d8610180526020610180600461019c845afa612381573d5f5f3e3d5ffd5b60203d1061522657610180518060a01c615226576101c0526101c090505163fb79eb276101e0526060806102005280610200015f6040518083528060051b5f82600881116152265780156123ee57905b8060051b606001518160051b6020880101526001018181186123d1575b50508201602001915050905081019050610160516102205230610240525060206101e06101846101fc845afa612426573d5f5f3e3d5ffd5b60203d10615226576101e09050f35b606461244160c061323c565b60c0510460e052602060e0f35b602061245a60c061323c565b60c0f35b602061246b6101e0613055565b6101e060043581518110156152265760051b60208201019050f35b602080610300526124986101e0613055565b6101e081610300015f82518083528060051b5f82600881116152265780156124db57905b8060051b6020880101518160051b6020880101526001018181186124bc575b505082016020019150509050905081019050610300f35b60208061036052612504610240612d26565b61024081610360015f82518083528060051b5f826008811161522657801561254757905b8060051b6020880101518160051b602088010152600101818118612528575b505082016020019150509050905081019050610360f35b60043580600f0b81186152265760405260243580600f0b811861522657606052602060206154335f395f5163e31593d8608052602060806004609c845afa6125a8573d5f5f3e3d5ffd5b60203d10615226576080518060a01c6152265760c05260c090505163a63530bd60e05260405161010052606051610120523061014052602060e0606460fc845afa6125f5573d5f5f3e3d5ffd5b60203d106152265760e09050f35b60206154335f395f5163f851a44060c052602060c0600460dc845afa61262b573d5f5f3e3d5ffd5b60203d106152265760c0518060a01c6152265761010052610100905051331861522657600e5462015180810181811061522657905042106152265742620151808101818110615226579050602435106152265761268860e061323c565b60e05160c0526004356064810281606482041861522657905060e052600435156126ba57620f423f60043511156126bc565b5f5b156152265760c05160e051106126ec5760c051600a810281600a82041861522657905060e0511161522657612708565b60c05160e051600a810281600a82041861522657905010615226575b60c051600c5560e051600d5542600e55602435600f557fa2b71ec6df949300b59aab36b55e189697b750119dd349fcfa8c0f779e83c25460c0516101005260e051610120524261014052602435610160526080610100a1005b60206154335f395f5163f851a44060c052602060c0600460dc845afa612789573d5f5f3e3d5ffd5b60203d106152265760c0518060a01c61522657610100526101009050513318615226576127b660e061323c565b60e05160c05260c051600c5560c051600d5542600e5542600f557f46e22fb3709ad289f62ce63d469248536dbc78d82b84a3d7e74ad606dc20193860c05160e0524261010052604060e0a1005b60206154335f395f5163f851a440604052602060406004605c845afa61282b573d5f5f3e3d5ffd5b60203d10615226576040518060a01c61522657608052608090505133186152265764012a05f2006004351161522657600435600a556802b5e3af16b188000060243560043580820281158383830414171561522657905090501161522657602435600b557f750d10a7f37466ce785ee6bcb604aac543358db42afbcc332a3c12a49c80bf6d6040600460403760406040a1005b60206154335f395f5163f851a440604052602060406004605c845afa6128e6573d5f5f3e3d5ffd5b60203d10615226576040518060a01c6152265760805260809050513318615226576024356004350215615226576004356023556024356024557f68dc4e067dff1862b896b7a0faf55f97df1a60d0aaa79481b69d675f2026a28c6040600460403760406040a1005b5f5ffd5b6fffffffffffffffffffffffffffffffff60405111615226576fffffffffffffffffffffffffffffffff606051116152265760605160801b60405117815250565b602060405160206154535f395f518110156152265760051b6080016153f3015f395f516370a0823160e0523061010052602060e0602460fc845afa6129da573d5f5f3e3d5ffd5b60203d106152265760e090505160c05260a051612af6576060511561522657602060405160206154535f395f518110156152265760051b6080016153f3015f395f516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc5f855af1612a4f573d5f5f3e3d5ffd5b3d612a6657803b1561522657600161016052612a7e565b60203d106152265760e0518060011c61522657610160525b6101609050511561522657602060405160206154535f395f518110156152265760051b6080016153f3015f395f516370a0823160e0523061010052602060e0602460fc845afa612ad0573d5f5f3e3d5ffd5b60203d106152265760e090505160c051808203828111615226579050905060c052612b27565b60c0516040516001548110156152265760020154808203828111615226579050905060c05260605160c05110615226575b60405160015481101561522657600201805460c051808201828110615226579050905081555060c051815250565b608051156152265760206156935f395f51612c1e576040516001548110156152265760020180546060518082038281116152265790509050815550602060405160206154535f395f518110156152265760051b6080016153f3015f395f5163a9059cbb60a05260805160c05260605160e052602060a0604460bc5f855af1612bdf573d5f5f3e3d5ffd5b3d612bf657803b1561522657600161010052612c0e565b60203d106152265760a0518060011c61522657610100525b6101009050511561522657612d24565b602060405160206154535f395f518110156152265760051b6080016153f3015f395f516370a0823160c0523060e052602060c0602460dc845afa612c64573d5f5f3e3d5ffd5b60203d106152265760c090505160a052602060405160206154535f395f518110156152265760051b6080016153f3015f395f5163a9059cbb60c05260805160e05260605161010052602060c0604460dc5f855af1612cc4573d5f5f3e3d5ffd5b3d612cdb57803b1561522657600161012052612cf3565b60203d106152265760c0518060011c61522657610120525b610120905051156152265760a051606051808203828111615226579050905060405160015481101561522657600201555b565b60206156b35f395f5160208160051b01806156b360403950505f60206154135f395f516008811161522657801561303a57905b8061016052600160206101605160206155735f395f518110156152265760051b6101a0016153f3015f395f5118612db65760206101605160206157d35f395f518110156152265760051b610400016153f3015f395f511515612db8565b5f5b612ee757600360206101605160206155735f395f518110156152265760051b6101a0016153f3015f395f511861302f57670de0b6b3a7640000610160516040518110156152265760051b6060015160206101605160206154535f395f518110156152265760051b6080016153f3015f395f516307a2d13a6101805260206101605160206158f35f395f518110156152265760051b610520016153f3016101a0396020610180602461019c845afa612e71573d5f5f3e3d5ffd5b60203d106152265761018090505180820281158383830414171561522657905090506020610160516020615a135f395f518110156152265760051b610640016153f3015f395f51808202811583838304141715615226579050905004610160516040518110156152265760051b6060015261302f565b60206101605160206157d35f395f518110156152265760051b610400016153f3015f395f5173ffffffffffffffffffffffffffffffffffffffff811690508060a01c615226575a7fffffffff0000000000000000000000000000000000000000000000000000000060206101605160206157d35f395f518110156152265760051b610400016153f3015f395f51166101e05260206101c0526101c05060206102206101c0516101e08585fa90509050612fa2573d5f5f3e3d5ffd5b3d602081183d60201002186102005261020080516101805260208101516101a0525060206101805118615226576101a0516101805160200360031b1c6101c052670de0b6b3a7640000610160516040518110156152265760051b606001516101c051808202811583838304141715615226579050905004610160516040518110156152265760051b606001525b600101818118612d59575b505060405160208160051b01808382604060045afa50505050565b5f6040525f610160525f60206154135f395f516008811161522657801561316d57905b806101805260206156935f395f516130c557610180516001548110156152265760020154610180516010548110156152265760110154808203828111615226579050905061016052613143565b60206101805160206154535f395f518110156152265760051b6080016153f3015f395f516370a082316101a052306101c05260206101a060246101bc845afa613110573d5f5f3e3d5ffd5b60203d10615226576101a09050516101805160105481101561522657601101548082038281116152265790509050610160525b6040516007811161522657610160518160051b606001526001810160405250600101818118613078575b505060405160208160051b01808382604060045afa50505050565b5f610280525f60206154135f395f516008811161522657801561321f57905b806103a052610280516007811161522657670de0b6b3a76400006103a0516040518110156152265760051b606001516103a051610160518110156152265760051b61018001518082028115838383041417156152265790509050048160051b6102a001526001810161028052506001018181186131a7575b50506102805160208160051b0180838261028060045afa50505050565b600f54604052600d5460605260405142106132605760605181525061334556613345565b600c54608052600e5460a052608051606051116132e257608051606051608051034260a0518082038281116152265790509050808202811583838304141715615226579050905060405160a051808203828111615226579050905080156152265780820490509050808203828111615226579050905081525061334556613345565b608051608051606051034260a0518082038281116152265790509050808202811583838304141715615226579050905060405160a05180820382811161522657905090508015615226578082049050905080820182811061522657905090508152505b565b5f610180525f6040516008811161522657801561339157905b8060051b606001516101a052610180516101a051808201828110615226579050905061018052600101818118613360575b5050610180516133a4575f8152506135bd565b610180516101a0526101605160206153f35f395f5180820281158383830414171561522657905090506101c0525f60ff905b806101e0526101a051610200525f6040516008811161522657801561343f57905b8060051b6060015161022052610200516101a05180820281158383830414171561522657905090506102205180156152265780820490509050610200526001018181186133f7575b50506102005160206153f35f395f5160206153f35f395f510a80156152265780820490509050610200526101a0516102205260646101c051610180518082028115838383041417156152265790509050046102005160206153f35f395f51808202811583838304141715615226579050905080820182811061522657905090506101a051808202811583838304141715615226579050905060646101c051606481038181116152265790506101a051808202811583838304141715615226579050905004600160206153f35f395f51016102005180820281158383830414171561522657905090508082018281106152265790509050801561522657808204905090506101a052610220516101a05111613582576001610220516101a0518082038281116152265790509050116135ad576101a05183525050506135bd566135ad565b60016101a051610220518082038281116152265790509050116135ad576101a05183525050506135bd565b6001018181186133d65750505f5ffd5b565b60605160405114615226575f606051126152265760206154135f395f516060511215615226575f604051126152265760206154135f395f516040511215615226576101c051610200526101e0516102205260603661024037610220516102a0526102005160206153f35f395f5180820281158383830414171561522657905090506102c0525f6008905b806102e05260206154135f395f516102e051186136655761371f565b6040516102e0511861367d57608051610260526136ab565b6060516102e05114613714576102e05160a0518110156152265760051b60c00151610260526136ab56613714565b61024051610260518082018281106152265790509050610240526102a0516102205180820281158383830414171561522657905090506102605160206153f35f395f518082028115838383041417156152265790509050801561522657808204905090506102a0525b600101818118613649575b50506102a051610220518082028115838383041417156152265790509050606481028160648204186152265790506102c05160206153f35f395f518082028115838383041417156152265790509050801561522657808204905090506102a0526102405161022051606481028160648204186152265790506102c0518015615226578082049050905080820182811061522657905090506102e05261022051610300525f60ff905b80610320526103005161028052610300516103005180820281158383830414171561522657905090506102a0518082018281106152265790509050610300518060011b818160011c186152265790506102e0518082018281106152265790509050610220518082038281116152265790509050801561522657808204905090506103005261028051610300511161388757600161028051610300518082038281116152265790509050116138b2576103005183525050506138c2566138b2565b600161030051610280518082038281116152265790509050116138b2576103005183525050506138c2565b6001018181186137c75750505f5ffd5b565b600b5460a0526402540be40060a051116138e357608051815250613982565b60405160605180820182811061522657905090506fffffffffffffffffffffffffffffffff8111615226576002810a905060c0526402540be4006402540be40060a051038060021b818160021c186152265790506040518082028115838383041417156152265790509050606051808202811583838304141715615226579050905060c051801561522657808204905090500160805160a05102048152505b565b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b60206153f35f395f5161016051026101a05260206153f35f395f5160206153f35f395f510a61018051046101c0525f60206154135f395f5160088111615226578015613a4357905b806101e0526101c0516101805180820281158383830414171561522657905090506101e0516040518110156152265760051b60600151801561522657808204905090506101c0526001018181186139f1575b50505f6101e05260646101a05160405115615226575f60051b6060015180820281158383830414171561522657905090500461030052600160078101905b806103205260206153f35f395f516103205118613a9d57613b57565b6101e051600781116152265761030051610320516040518110156152265760051b606001516101c05160405115615226575f60051b606001518082028115838383041417156152265790509050048082018281106152265790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615226579050610300516101c0518082018281106152265790509050801561522657808204905090508160051b6102000152600181016101e05250600101818118613a81575b50506101e05160208160051b018083826101e060045afa50505050565b6040516060527ffffffffffffffffffffffffffffffffffffffffffffffffdc0d0570925a462d760405113613bac575f815250613dc4565b680755bf798b4a1bf1e46040511315613c1b5760106080527f7761645f657870206f766572666c6f770000000000000000000000000000000060a0526080506080518060a001601f825f031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b6503782dace9d9604051604e1b056060526b8000000000000000000000006bb17217f7d1cf79abc9e3b39860605160601b050160601d6080526bb17217f7d1cf79abc9e3b39860805102606051036060526d02d16720577bd19bf614176fe9ea6060516c10fe68e7fd37d0007b713f7650606051010260601d0160a05279d835ebba824c98fb31b83b2ca45c0000000000000000000000006060516e0587f503bb6ea29d25fcb74019645060a0516d04a4fd9f2a8b96949216d2255a6c60605160a05101030260601d01020160c0526d0277594991cfc85f6e2461837cd96060516c240c330e9fb2d9cbaf0fd5aafc606051030260601d0160e0526d1a521255e34f6a5061b25ef1c9c460605160e0510260601d0360e0526db1bbb201f443cf962f1a1d3db4a560605160e0510260601d0160e0526e02c72388d9f74f51a9331fed693f1560605160e0510260601d0360e0526e05180bb14799ab47a8a8cb2a527d5760605160e0510260601d0160e05260e05160c051056101005274029d9dc38563c32e5c2f6dc192ee70ef65f9978af3610100510260805160c3035f8112615226571c8152505b565b6fffffffffffffffffffffffffffffffff6101205116610180526101205160801c6101a05242610160511015613ec65761014051670de0b6b3a764000061016051420302048060ff1c615226577f80000000000000000000000000000000000000000000000000000000000000008114615226575f03604052613e4a6101e0613b74565b6101e0516101c052670de0b6b3a7640000610180516101c05180670de0b6b3a764000003670de0b6b3a7640000811161522657905080820281158383830414171561522657905090506101a0516101c0518082028115838383041417156152265790509050808201828110615226579050905004815250613ece565b6101a0518152505b565b602554604052613ee16104e0613984565b6104e060406104a060408360045afa505060195460208160051b015f81601f0160051c60098111615226578015613f2d57905b80601901548160051b6104e00152600101818118613f14575b505050506104e05160208160051b0180610600826104e060045afa5050506103405160208160051b018060408261034060045afa50505061046051610160526104805161018052613f7f6108406139a9565b610840805160208160051b0180610720828560045afa505050505f6008905b806108405260206153f35f395f51600181038181116152265790506108405118613fc757614096565b61084051610720518110156152265760051b61074001511561408b5761084051610720518110156152265760051b6107400151671bc16d674ec80000818118671bc16d674ec800008310021890506108a052610840516104e0518110156152265760051b610500015161012052602354610140526104a0516101605261404e610860613dc6565b610860516108c0526040604060406108a060045afa5061406f610880612952565b6108805161084051610600518110156152265760051b61062001525b600101818118613f9e575b50506106005160208160051b015f81601f0160051c600981116152265780156140d457905b8060051b610600015181601901556001018181186140bb575b5050505060225461084052610480516108a0526108405161012052602454610140526104c05161016052614109610860613dc6565b610860516108c0526040604060406108a060045afa5061412a610880612952565b610880516022555f6002905b80610860524261086051600181116152265760051b6104a00151101561416c574261086051600181116152265760051b6104a001525b6001018181186141365750506104a0516040526104c051606052614191610860612952565b61086051602555565b6141a5610c2061323c565b610c2051610c00526109805160208160051b018060408261098060045afa505050610c0051610160526141d9610c40613347565b610c4051610c2052610bc051604052610be051606052610960516080526109805160208160051b018060a08261098060045afa505050610c00516101c052610c20516101e05261422a610c606135bf565b610c6051610c4052610be051610980518110156152265760051b6109a00151610c4051808203828111615226579050905060018103818111615226579050610c60526402540be400610c6051610bc051610980518110156152265760051b6109a0015161096051808201828110615226579050905060011c604052610be051610980518110156152265760051b6109a00151610c4051808201828110615226579050905060011c606052600a546080526142e5610ca06138c4565b610ca051808202811583838304141715615226579050905004610c8052610c6051610c80518082038281116152265790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615226579050610be051610aa0518110156152265760051b610ac0015180156152265780820490509050610c6052610be051601054811015615226576011018054610be051610aa0518110156152265760051b610ac001516402540be400610c805164012a05f20081028164012a05f20082041861522657905004670de0b6b3a7640000810281670de0b6b3a76400008204186152265790500480820182811061522657905090508155506109805160208160051b0180610ca08261098060045afa50505061096051610bc051610ca0518110156152265760051b610cc00152610c4051610be051610ca0518110156152265760051b610cc00152610ca05160208160051b018061034082610ca060045afa505050610c005161046052610c20516104805261445d613ed0565b610c6051815250565b610e0051610de0511461522657610e20511561522657614487610fc0612d26565b610fc0805160208160051b0180610ea0828560045afa505050506144ac6110e0613055565b6110e0805160208160051b0180610fc0828560045afa50505050610ea05160208160051b0180604082610ea060045afa505050610fc05160208160051b018061016082610fc060045afa505050614504611200613188565b611200805160208160051b01806110e0828560045afa50505050610de051604052610e2051606052610dc051608052610e805160a052614545611220612993565b6112205161120052610de0516110e0518110156152265760051b6111000151670de0b6b3a764000061120051610de051610ea0518110156152265760051b610ec0015180820281158383830414171561522657905090500480820182811061522657905090506112205261122051610960526110e05160208160051b0180610980826110e060045afa505050610ea05160208160051b0180610aa082610ea060045afa505050610de051610bc052610e0051610be05261460661126061419a565b6112605161124052610e40516112405110156146a657602e611260527f45786368616e676520726573756c74656420696e20666577657220636f696e73611280527f207468616e2065787065637465640000000000000000000000000000000000006112a05261126050611260518061128001601f825f031636823750506308c379a061122052602061124052601f19601f61126051011660440161123cfd5b610e005160405261124051606052610e60516080526146c3612b55565b337f8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140610de051611260526112005161128052610e00516112a052611240516112c0526080611260a261124051815250565b6103c05160208160051b01806040826103c060045afa5050506104e05160208160051b0180610160826104e060045afa505050614752610740613188565b610740805160208160051b0180610620828560045afa505050506106205160208160051b018060408261062060045afa5050506106005161016052614798610740613347565b61074051815250565b5f606051126152265760206154135f395f516060511215615226576060366101c0376101a0516102205260405160206153f35f395f518082028115838383041417156152265790509050610240525f6008905b806102605260206154135f395f516102605118614810576148b2565b60605161026051146148a757610260516080518110156152265760051b60a001516101e05261483e566148a7565b6101c0516101e05180820182811061522657905090506101c052610220516101a05180820281158383830414171561522657905090506101e05160206153f35f395f51808202811583838304141715615226579050905080156152265780820490509050610220525b6001018181186147f4575b5050610220516101a0518082028115838383041417156152265790509050606481028160648204186152265790506102405160206153f35f395f51808202811583838304141715615226579050905080156152265780820490509050610220526101c0516101a0516064810281606482041861522657905061024051801561522657808204905090508082018281106152265790509050610260526101a051610280525f60ff905b806102a052610280516102005261028051610280518082028115838383041417156152265790509050610220518082018281106152265790509050610280518060011b818160011c186152265790506102605180820182811061522657905090506101a05180820382811161522657905090508015615226578082049050905061028052610200516102805111614a1a5760016102005161028051808203828111615226579050905011614a4557610280518352505050614a5556614a45565b60016102805161020051808203828111615226579050905011614a4557610280518352505050614a55565b60010181811861495a5750505f5ffd5b565b614a6261042061323c565b6104205161040052614a75610540612d26565b610540805160208160051b0180610420828560045afa505050506104205160208160051b01806108a08261042060045afa505050614ab4610660613055565b610660805160208160051b01806109c0828560045afa5050505061024060406102406108a060045afa50614ae9610780613188565b610780805160208160051b0180610540828560045afa505050506105405160208160051b018060408261054060045afa5050506104005161016052614b2f610680613347565b610680516106605260285461068052610660516103c051610660518082028115838383041417156152265790509050610680518015615226578082049050905080820382811161522657905090506106a052610400516040526103e0516060526105405160208160051b018060808261054060045afa5050506106a0516101a052614bbb6106e06147a1565b6106e0516106c052600160206153f35f395f510360021b60206153f35f395f51600a5402046106e0526105405160208160051b01806107008261054060045afa50505060206153f35f395f5160011b610660516106a05180820182811061522657905090500461082052608036610840375f6008905b806108c05260206154135f395f516108c05118614c4d57614daa565b5f610840526108c051610540518110156152265760051b6105600151610860526103e0516108c05118614cdf57610860516106a051808202811583838304141715615226579050905061066051801561522657808204905090506106c051808203828111615226579050905061084052610860516106c051808201828110615226579050905060011c61088052614d2b565b61086051610860516106a0518082028115838383041417156152265790509050610660518015615226578082049050905080820382811161522657905090506108405261086051610880525b61088051604052610820516060526106e051608052614d4b6108e06138c4565b6108e0516108a052610860516402540be4006108a0516108405180820281158383830414171561522657905090500480820382811161522657905090506108c051610700518110156152265760051b6107200152600101818118614c31575b50506103e051610700518110156152265760051b6107200151610400516040526103e0516060526107005160208160051b018060808261070060045afa5050506106a0516101a052614dfd6108e06147a1565b6108e05180820382811161522657905090506108c0526103e051610540518110156152265760051b61056001516106c0518082038281116152265790509050670de0b6b3a7640000810281670de0b6b3a76400008204186152265790506103e051610420518110156152265760051b6104400151801561522657808204905090506108e0526103e051610420518110156152265760051b61044001516108c05160018103818111615226579050670de0b6b3a7640000810281670de0b6b3a7640000820418615226579050046108c0526106c0516103e051610540518110156152265760051b61056001526108c05181526108e0516108c051808203828111615226579050905060208201526105405160208160051b016040830181818361054060045afa50505050610400516101608201526106a05161018082015250565b602854606051808203828111615226579050905060285560266040516020525f5260405f20805460605180820382811161522657905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a3565b60206154335f395f5163cab4d3db610160526020610160600461017c845afa614fd5573d5f5f3e3d5ffd5b60203d1061522657610160518060a01c615226576101a0526101a0905051610140526101405161500457615115565b60105460208160051b015f81601f0160051c6009811161522657801561503f57905b80601001548160051b6101600152600101818118615026575b505050505f60206154135f395f51600881116152265780156150d257905b806102805261028051610160518110156152265760051b6101800151156150c7576102805160405261028051610160518110156152265760051b6101800151606052610140516080526150ae612b55565b5f61028051610160518110156152265760051b61018001525b60010181811861505d575b50506101605160208160051b015f81601f0160051c6009811161522657801561511057905b8060051b610160015181601001556001018181186150f7575b505050505b565b6020615bf35f395f51461461519f577fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac564726060526020615bd36080397f1c54f243822e0e9a0a377610b81577e771b3efe79964e76636b0d5d10247950d60a0524660c0523060e0526020615c136101003960c060405260408051602082012090508152506151a8565b6020615c338239505b565b60266040516020525f5260405f208054608051808203828111615226579050905081555060266060516020525f5260405f20805460805180820182811061522657905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b5f80fd0299538309065752a30a809452480d1cfc52e90c008153c207076c533d0ad40ddb8c1550652081066c00dc059c4258c4011a053931ab5219c72554fd4d5001c90576a9cd3e255e455e604cd2156085b72df5de03f4651a4d01d20bff6567df02ca207a6529357750006705e2e7d26400f425ec0238621a1025fd0684b124f20518160ddd220405a9059cbb1baa4570a0823102372530c5408519ac05ddca3f43009d055e0d443f21266506fdde03013205a7256d0903fe85c66106570075258edfdd5f00a905ddc1f59d02dea5cc2b27d721d24590d2083719f3253644e515206805313ce56701be05081579a50c09851ddc3b01012605d505accf1d3de5bfa0b13302c60595d89b410178051be913a5010e057706db750e1765fee3f7f900b505095ea7b31cd54514f0597924860565bbea6b28be45907a016b1b7705b4b577ad00d0054903b0d1245e2523b872dd1bdb65687276531b27252969e04a157aa5551a65882761051405228800e8055409491a00c405015c28382803454a6e32c60e21857ecebe0002a0257e3db030035d85dd62ed3e025d45bb7b8b802219053c157e6426034576a2f0f0244e053df0212402d485f446c1d02435053db06dd8232565afb430120367a5000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000006a8cbed756804b16e05e741edabd5cb544ae21bf00000000000000000000000000000000000000000000000000000000000000020000000000000000000000002b01d4fdb87eb7f2399e5d68631d8a939a32378600000000000000000000000004c154b66cb340f3ae24111cc767e0184ed00cc6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a784554482f707845544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097845544870784554480000000000000000000000000000000000000000000000be9086b9f5dabf01030af51211082676c110f917afbfc1c8d6860adea260c275000000000000000000000000000000000000000000000000000000000000000165ac406af4a64c76e3ddd7b1489b2d1f444adeb305bba3772e761d5e125172cde6cf574e99e7583122317f280bf8f0c535a91248e38f234ed6e1270dd215bf02

Verified Source Code Partial Match

Compiler: v0.3.10+commit.91361694
CurveStableSwapNG.vy 1890 lines
# pragma version 0.3.10
# pragma optimize codesize
# pragma evm-version shanghai
"""
@title CurveStableSwapNG
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
@notice Stableswap implementation for up to 8 coins with no rehypothecation,
        i.e. the AMM does not deposit tokens into other contracts. The Pool contract also
        records exponential moving averages for coins relative to coin 0.
@dev Asset Types:
        0. Standard ERC20 token with no additional features.
                          Note: Users are advised to do careful due-diligence on
                                ERC20 tokens that they interact with, as this
                                contract cannot differentiate between harmless and
                                malicious ERC20 tokens.
        1. Oracle - token with rate oracle (e.g. wstETH)
                    Note: Oracles may be controlled externally by an EOA. Users
                          are advised to proceed with caution.
        2. Rebasing - token with rebase (e.g. stETH).
                      Note: Users and Integrators are advised to understand how
                            the AMM contract works with rebasing balances.
        3. ERC4626 - token with convertToAssets method (e.g. sDAI).
                     Note: Some ERC4626 implementations may be susceptible to
                           Donation/Inflation attacks. Users are advised to
                           proceed with caution.
        NOTE: Pool Cannot support tokens with multiple asset types: e.g. ERC4626
              with fees are not supported.
     Supports:
        1. ERC20 support for return True/revert, return True/False, return None
        2. ERC20 tokens can have arbitrary decimals (<=18).
        3. ERC20 tokens that rebase (either positive or fee on transfer)
        4. ERC20 tokens that have a rate oracle (e.g. wstETH, cbETH, sDAI, etc.)
           Note: Oracle precision _must_ be 10**18.
        5. ERC4626 tokens with arbitrary precision (<=18) of Vault token and underlying
           asset.
     Additional features include:
        1. Adds price oracles based on AMM State Price (and _not_ last traded price).
        2. Adds TVL oracle based on D.
        3. `exchange_received`: swaps that expect an ERC20 transfer to have occurred
           prior to executing the swap.
           Note: a. If pool contains rebasing tokens and one of the `asset_types` is 2 (Rebasing)
                    then calling `exchange_received` will REVERT.
                 b. If pool contains rebasing token and `asset_types` does not contain 2 (Rebasing)
                    then this is an incorrect implementation and rebases can be
                    stolen.
        4. Adds `get_dx`: Similar to `get_dy` which returns an expected output
           of coin[j] for given `dx` amount of coin[i], `get_dx` returns expected
           input of coin[i] for an output amount of coin[j].
        5. Fees are dynamic: AMM will charge a higher fee if pool depegs. This can cause very
                             slight discrepancies between calculated fees and realised fees.
"""

from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed
from vyper.interfaces import ERC4626

implements: ERC20

# ------------------------------- Interfaces ---------------------------------

interface Factory:
    def fee_receiver() -> address: view
    def admin() -> address: view
    def views_implementation() -> address: view

interface ERC1271:
    def isValidSignature(_hash: bytes32, _signature: Bytes[65]) -> bytes32: view

interface StableSwapViews:
    def get_dx(i: int128, j: int128, dy: uint256, pool: address) -> uint256: view
    def get_dy(i: int128, j: int128, dx: uint256, pool: address) -> uint256: view
    def dynamic_fee(i: int128, j: int128, pool: address) -> uint256: view
    def calc_token_amount(
        _amounts: DynArray[uint256, MAX_COINS],
        _is_deposit: bool,
        _pool: address
    ) -> uint256: view

# --------------------------------- Events -----------------------------------

event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

event Approval:
    owner: indexed(address)
    spender: indexed(address)
    value: uint256

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: DynArray[uint256, MAX_COINS]
    fees: DynArray[uint256, MAX_COINS]
    invariant: uint256
    token_supply: uint256

event RemoveLiquidity:
    provider: indexed(address)
    token_amounts: DynArray[uint256, MAX_COINS]
    fees: DynArray[uint256, MAX_COINS]
    token_supply: uint256

event RemoveLiquidityOne:
    provider: indexed(address)
    token_id: int128
    token_amount: uint256
    coin_amount: uint256
    token_supply: uint256

event RemoveLiquidityImbalance:
    provider: indexed(address)
    token_amounts: DynArray[uint256, MAX_COINS]
    fees: DynArray[uint256, MAX_COINS]
    invariant: uint256
    token_supply: uint256

event RampA:
    old_A: uint256
    new_A: uint256
    initial_time: uint256
    future_time: uint256

event StopRampA:
    A: uint256
    t: uint256

event ApplyNewFee:
    fee: uint256
    offpeg_fee_multiplier: uint256

event SetNewMATime:
    ma_exp_time: uint256
    D_ma_time: uint256


MAX_COINS: constant(uint256) = 8  # max coins is 8 in the factory
MAX_COINS_128: constant(int128) = 8

# ---------------------------- Pool Variables --------------------------------

N_COINS: public(immutable(uint256))
N_COINS_128: immutable(int128)
PRECISION: constant(uint256) = 10 ** 18

factory: immutable(Factory)
coins: public(immutable(DynArray[address, MAX_COINS]))
asset_types: immutable(DynArray[uint8, MAX_COINS])
pool_contains_rebasing_tokens: immutable(bool)
stored_balances: DynArray[uint256, MAX_COINS]

# Fee specific vars
FEE_DENOMINATOR: constant(uint256) = 10 ** 10
fee: public(uint256)  # fee * 1e10
offpeg_fee_multiplier: public(uint256)  # * 1e10
admin_fee: public(constant(uint256)) = 5000000000
MAX_FEE: constant(uint256) = 5 * 10 ** 9

# ---------------------- Pool Amplification Parameters -----------------------

A_PRECISION: constant(uint256) = 100
MAX_A: constant(uint256) = 10 ** 6
MAX_A_CHANGE: constant(uint256) = 10

initial_A: public(uint256)
future_A: public(uint256)
initial_A_time: public(uint256)
future_A_time: public(uint256)

# ---------------------------- Admin Variables -------------------------------

MIN_RAMP_TIME: constant(uint256) = 86400
admin_balances: public(DynArray[uint256, MAX_COINS])

# ----------------------- Oracle Specific vars -------------------------------

rate_multipliers: immutable(DynArray[uint256, MAX_COINS])
# [bytes4 method_id][bytes8 <empty>][bytes20 oracle]
rate_oracles: immutable(DynArray[uint256, MAX_COINS])

# For ERC4626 tokens, we need:
call_amount: immutable(DynArray[uint256, MAX_COINS])
scale_factor: immutable(DynArray[uint256, MAX_COINS])

last_prices_packed: DynArray[uint256, MAX_COINS]  #  packing: last_price, ma_price
last_D_packed: uint256                            #  packing: last_D, ma_D
ma_exp_time: public(uint256)
D_ma_time: public(uint256)
ma_last_time: public(uint256)                     # packing: ma_last_time_p, ma_last_time_D
# ma_last_time has a distinction for p and D because p is _not_ updated if
# users remove_liquidity, but D is.

# shift(2**32 - 1, 224)
ORACLE_BIT_MASK: constant(uint256) = (2**32 - 1) * 256**28

# --------------------------- ERC20 Specific Vars ----------------------------

name: public(immutable(String[64]))
symbol: public(immutable(String[32]))
decimals: public(constant(uint8)) = 18
version: public(constant(String[8])) = "v7.0.0"

balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
total_supply: uint256
nonces: public(HashMap[address, uint256])

# keccak256("isValidSignature(bytes32,bytes)")[:4] << 224
ERC1271_MAGIC_VAL: constant(bytes32) = 0x1626ba7e00000000000000000000000000000000000000000000000000000000
EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
EIP2612_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")

VERSION_HASH: constant(bytes32) = keccak256(version)
NAME_HASH: immutable(bytes32)
CACHED_CHAIN_ID: immutable(uint256)
salt: public(immutable(bytes32))
CACHED_DOMAIN_SEPARATOR: immutable(bytes32)


# ------------------------------ AMM Setup -----------------------------------


@external
def __init__(
    _name: String[32],
    _symbol: String[10],
    _A: uint256,
    _fee: uint256,
    _offpeg_fee_multiplier: uint256,
    _ma_exp_time: uint256,
    _coins: DynArray[address, MAX_COINS],
    _rate_multipliers: DynArray[uint256, MAX_COINS],
    _asset_types: DynArray[uint8, MAX_COINS],
    _method_ids: DynArray[bytes4, MAX_COINS],
    _oracles: DynArray[address, MAX_COINS],
):
    """
    @notice Initialize the pool contract
    @param _name Name of the new plain pool.
    @param _symbol Symbol for the new plain pool.
    @param _A Amplification co-efficient - a lower value here means
              less tolerance for imbalance within the pool's assets.
              Suggested values include:
               * Uncollateralized algorithmic stablecoins: 5-10
               * Non-redeemable, collateralized assets: 100
               * Redeemable assets: 200-400
    @param _fee Trade fee, given as an integer with 1e10 precision. The
                the maximum is 1% (100000000).
                50% of the fee is distributed to veCRV holders.
    @param _offpeg_fee_multiplier A multiplier that determines how much to increase
                                  Fees by when assets in the AMM depeg. Example value: 20000000000
    @param _ma_exp_time Averaging window of oracle. Set as time_in_seconds / ln(2)
                        Example: for 10 minute EMA, _ma_exp_time is 600 / ln(2) ~= 866
    @param _coins List of addresses of the coins being used in the pool.
    @param _rate_multipliers An array of: [10 ** (36 - _coins[n].decimals()), ... for n in range(N_COINS)]
    @param _asset_types Array of uint8 representing tokens in pool
    @param _method_ids Array of first four bytes of the Keccak-256 hash of the function signatures
                       of the oracle addresses that gives rate oracles.
                       Calculated as: keccak(text=event_signature.replace(" ", ""))[:4]
    @param _oracles Array of rate oracle addresses.
    """

    coins = _coins
    asset_types = _asset_types
    pool_contains_rebasing_tokens = 2 in asset_types
    __n_coins: uint256 = len(_coins)
    N_COINS = __n_coins
    N_COINS_128 = convert(__n_coins, int128)

    rate_multipliers = _rate_multipliers

    factory = Factory(msg.sender)

    A: uint256 = unsafe_mul(_A, A_PRECISION)
    self.initial_A = A
    self.future_A = A
    self.fee = _fee
    self.offpeg_fee_multiplier = _offpeg_fee_multiplier

    assert _ma_exp_time != 0
    self.ma_exp_time = _ma_exp_time
    self.D_ma_time = 62324  # <--------- 12 hours default on contract start.
    self.ma_last_time = self.pack_2(block.timestamp, block.timestamp)

    #  ------------------- initialize storage for DynArrays ------------------

    _call_amount: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    _scale_factor: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    _rate_oracles: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if i < N_COINS_128 - 1:
            self.last_prices_packed.append(self.pack_2(10**18, 10**18))

        _rate_oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
        self.stored_balances.append(0)
        self.admin_balances.append(0)

        if _asset_types[i] == 3:

            _call_amount.append(10**convert(ERC20Detailed(_coins[i]).decimals(), uint256))
            _underlying_asset: address = ERC4626(_coins[i]).asset()
            _scale_factor.append(10**(18 - convert(ERC20Detailed(_underlying_asset).decimals(), uint256)))

        else:

            _call_amount.append(0)
            _scale_factor.append(0)

    call_amount = _call_amount
    scale_factor = _scale_factor
    rate_oracles = _rate_oracles

    # ----------------------------- ERC20 stuff ------------------------------

    name = _name
    symbol = _symbol

    # EIP712 related params -----------------
    NAME_HASH = keccak256(name)
    salt = block.prevhash
    CACHED_CHAIN_ID = chain.id
    CACHED_DOMAIN_SEPARATOR = keccak256(
        _abi_encode(
            EIP712_TYPEHASH,
            NAME_HASH,
            VERSION_HASH,
            chain.id,
            self,
            salt,
        )
    )

    # ------------------------ Fire a transfer event -------------------------

    log Transfer(empty(address), msg.sender, 0)


# ------------------ Token transfers in and out of the AMM -------------------


@internal
def _transfer_in(
    coin_idx: int128,
    dx: uint256,
    sender: address,
    expect_optimistic_transfer: bool,
) -> uint256:
    """
    @notice Contains all logic to handle ERC20 token transfers.
    @param coin_idx Index of the coin to transfer in.
    @param dx amount of `_coin` to transfer into the pool.
    @param sender address to transfer `_coin` from.
    @param receiver address to transfer `_coin` to.
    @param expect_optimistic_transfer True if contract expects an optimistic coin transfer
    """
    _dx: uint256 = ERC20(coins[coin_idx]).balanceOf(self)

    # ------------------------- Handle Transfers -----------------------------

    if expect_optimistic_transfer:

        _dx = _dx - self.stored_balances[coin_idx]
        assert _dx >= dx

    else:

        assert dx > 0  # dev : do not transferFrom 0 tokens into the pool
        assert ERC20(coins[coin_idx]).transferFrom(
            sender, self, dx, default_return_value=True
        )

        _dx = ERC20(coins[coin_idx]).balanceOf(self) - _dx

    # --------------------------- Store transferred in amount ---------------------------

    self.stored_balances[coin_idx] += _dx

    return _dx


@internal
def _transfer_out(_coin_idx: int128, _amount: uint256, receiver: address):
    """
    @notice Transfer a single token from the pool to receiver.
    @dev This function is called by `remove_liquidity` and
         `remove_liquidity_one_coin`, `_exchange`, `_withdraw_admin_fees` and
         `remove_liquidity_imbalance` methods.
    @param _coin_idx Index of the token to transfer out
    @param _amount Amount of token to transfer out
    @param receiver Address to send the tokens to
    """
    assert receiver != empty(address)  # dev: do not send tokens to zero_address

    if not pool_contains_rebasing_tokens:

        # we need not cache balanceOf pool before swap out
        self.stored_balances[_coin_idx] -= _amount
        assert ERC20(coins[_coin_idx]).transfer(
            receiver, _amount, default_return_value=True
        )

    else:

        # cache balances pre and post to account for fee on transfers etc.
        coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self)
        assert ERC20(coins[_coin_idx]).transfer(
            receiver, _amount, default_return_value=True
        )
        self.stored_balances[_coin_idx] = coin_balance - _amount


# -------------------------- AMM Special Methods -----------------------------


@view
@internal
def _stored_rates() -> DynArray[uint256, MAX_COINS]:
    """
    @notice Gets rate multipliers for each coin.
    @dev If the coin has a rate oracle that has been properly initialised,
         this method queries that rate by static-calling an external
         contract.
    """
    rates: DynArray[uint256, MAX_COINS] = rate_multipliers

    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if asset_types[i] == 1 and not rate_oracles[i] == 0:

            # NOTE: fetched_rate is assumed to be 10**18 precision
            oracle_response: Bytes[32] = raw_call(
                convert(rate_oracles[i] % 2**160, address),
                _abi_encode(rate_oracles[i] & ORACLE_BIT_MASK),
                max_outsize=32,
                is_static_call=True,
            )
            assert len(oracle_response) == 32
            fetched_rate: uint256 = convert(oracle_response, uint256)

            rates[i] = unsafe_div(rates[i] * fetched_rate, PRECISION)

        elif asset_types[i] == 3:  # ERC4626

            # fetched_rate: uint256 = ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i]
            # here: call_amount has ERC4626 precision, but the returned value is scaled up to 18
            # using scale_factor which is (18 - n) if underlying asset has n decimals.
            rates[i] = unsafe_div(
                rates[i] * ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i],
                PRECISION
            )  # 1e18 precision

    return rates


@view
@internal
def _balances() -> DynArray[uint256, MAX_COINS]:
    """
    @notice Calculates the pool's balances _excluding_ the admin's balances.
    @dev If the pool contains rebasing tokens, this method ensures LPs keep all
            rebases and admin only claims swap fees. This also means that, since
            admin's balances are stored in an array and not inferred from read balances,
            the fees in the rebasing token that the admin collects is immune to
            slashing events.
    """
    result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    balances_i: uint256 = 0

    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if pool_contains_rebasing_tokens:
            # Read balances by gulping to account for rebases
            balances_i = ERC20(coins[i]).balanceOf(self) - self.admin_balances[i]
        else:
            # Use cached balances
            balances_i = self.stored_balances[i] - self.admin_balances[i]

        result.append(balances_i)

    return result


# -------------------------- AMM Main Functions ------------------------------


@external
@nonreentrant('lock')
def exchange(
    i: int128,
    j: int128,
    _dx: uint256,
    _min_dy: uint256,
    _receiver: address = msg.sender,
) -> 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 value of the coin to receive
    @param _dx Amount of `i` being exchanged
    @param _min_dy Minimum amount of `j` to receive
    @param _receiver Address that receives `j`
    @return Actual amount of `j` received
    """
    return self._exchange(
        msg.sender,
        i,
        j,
        _dx,
        _min_dy,
        _receiver,
        False
    )


@external
@nonreentrant('lock')
def exchange_received(
    i: int128,
    j: int128,
    _dx: uint256,
    _min_dy: uint256,
    _receiver: address = msg.sender,
) -> uint256:
    """
    @notice Perform an exchange between two coins without transferring token in
    @dev The contract swaps tokens based on a change in balance of coin[i]. The
         dx = ERC20(coin[i]).balanceOf(self) - self.stored_balances[i]. Users of
         this method are dex aggregators, arbitrageurs, or other users who do not
         wish to grant approvals to the contract: they would instead send tokens
         directly to the contract and call `exchange_received`.
         Note: This is disabled if pool contains rebasing tokens.
    @param i Index value for the coin to send
    @param j Index value of the coin to receive
    @param _dx Amount of `i` being exchanged
    @param _min_dy Minimum amount of `j` to receive
    @param _receiver Address that receives `j`
    @return Actual amount of `j` received
    """
    assert not pool_contains_rebasing_tokens  # dev: exchange_received not supported if pool contains rebasing tokens
    return self._exchange(
        msg.sender,
        i,
        j,
        _dx,
        _min_dy,
        _receiver,
        True,  # <--------------------------------------- swap optimistically.
    )


@external
@nonreentrant('lock')
def add_liquidity(
    _amounts: DynArray[uint256, MAX_COINS],
    _min_mint_amount: uint256,
    _receiver: address = msg.sender
) -> 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
    @param _receiver Address that owns the minted LP tokens
    @return Amount of LP tokens received by depositing
    """
    assert _receiver != empty(address)  # dev: do not send LP tokens to zero_address

    amp: uint256 = self._A()
    old_balances: DynArray[uint256, MAX_COINS] = self._balances()
    rates: DynArray[uint256, MAX_COINS] = self._stored_rates()

    # Initial invariant
    D0: uint256 = self.get_D_mem(rates, old_balances, amp)

    total_supply: uint256 = self.total_supply
    new_balances: DynArray[uint256, MAX_COINS] = old_balances

    # -------------------------- Do Transfers In -----------------------------

    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if _amounts[i] > 0:

            new_balances[i] += self._transfer_in(
                i,
                _amounts[i],
                msg.sender,
                False,  # expect_optimistic_transfer
            )

        else:

            assert total_supply != 0  # dev: initial deposit requires all coins

    # ------------------------------------------------------------------------

    # 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: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    mint_amount: uint256 = 0

    if total_supply > 0:

        ideal_balance: uint256 = 0
        difference: uint256 = 0
        new_balance: uint256 = 0

        ys: uint256 = unsafe_div(D0 + D1, N_COINS)
        xs: uint256 = 0
        _dynamic_fee_i: uint256 = 0

        # Only account for fees if we are not the first to deposit
        base_fee: uint256 = unsafe_div(
            unsafe_mul(self.fee, N_COINS),
            unsafe_mul(4, unsafe_sub(N_COINS, 1))
        )

        for i in range(N_COINS_128, bound=MAX_COINS_128):

            ideal_balance = D1 * old_balances[i] / D0
            difference = 0
            new_balance = new_balances[i]

            if ideal_balance > new_balance:
                difference = unsafe_sub(ideal_balance, new_balance)
            else:
                difference = unsafe_sub(new_balance, ideal_balance)

            # fee[i] = _dynamic_fee(i, j) * difference / FEE_DENOMINATOR
            xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
            _dynamic_fee_i = self._dynamic_fee(xs, ys, base_fee)
            fees.append(unsafe_div(_dynamic_fee_i * difference, FEE_DENOMINATOR))
            self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
            new_balances[i] -= fees[i]

        xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, new_balances)
        D1 = self.get_D(xp, amp)  # <--------------- Reuse D1 for new D value.
        mint_amount = unsafe_div(total_supply * (D1 - D0), D0)
        self.upkeep_oracles(xp, amp, D1)

    else:

        mint_amount = D1  # Take the dust if there was any

        # (re)instantiate D oracle if totalSupply is zero.
        self.last_D_packed = self.pack_2(D1, D1)

        # Update D ma time:
        ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
        if ma_last_time_unpacked[1] < block.timestamp:
            ma_last_time_unpacked[1] = block.timestamp
            self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])

    assert mint_amount >= _min_mint_amount, "Slippage screwed you"

    # Mint pool tokens
    total_supply += mint_amount
    self.balanceOf[_receiver] += mint_amount
    self.total_supply = total_supply
    log Transfer(empty(address), _receiver, mint_amount)

    log AddLiquidity(msg.sender, _amounts, fees, D1, total_supply)

    return mint_amount


@external
@nonreentrant('lock')
def remove_liquidity_one_coin(
    _burn_amount: uint256,
    i: int128,
    _min_received: uint256,
    _receiver: address = msg.sender,
) -> uint256:
    """
    @notice Withdraw a single coin from the pool
    @param _burn_amount Amount of LP tokens to burn in the withdrawal
    @param i Index value of the coin to withdraw
    @param _min_received Minimum amount of coin to receive
    @param _receiver Address that receives the withdrawn coins
    @return Amount of coin received
    """
    assert _burn_amount > 0  # dev: do not remove 0 LP tokens
    dy: uint256 = 0
    fee: uint256 = 0
    xp: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    amp: uint256 = empty(uint256)
    D: uint256 = empty(uint256)

    dy, fee, xp, amp, D = self._calc_withdraw_one_coin(_burn_amount, i)
    assert dy >= _min_received, "Not enough coins removed"

    self.admin_balances[i] += unsafe_div(fee * admin_fee, FEE_DENOMINATOR)

    self._burnFrom(msg.sender, _burn_amount)

    self._transfer_out(i, dy, _receiver)

    log RemoveLiquidityOne(msg.sender, i, _burn_amount, dy, self.total_supply)

    self.upkeep_oracles(xp, amp, D)

    return dy


@external
@nonreentrant('lock')
def remove_liquidity_imbalance(
    _amounts: DynArray[uint256, MAX_COINS],
    _max_burn_amount: uint256,
    _receiver: address = msg.sender
) -> 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
    @param _receiver Address that receives the withdrawn coins
    @return Actual amount of the LP token burned in the withdrawal
    """
    amp: uint256 = self._A()
    rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
    old_balances: DynArray[uint256, MAX_COINS] = self._balances()
    D0: uint256 = self.get_D_mem(rates, old_balances, amp)
    new_balances: DynArray[uint256, MAX_COINS] = old_balances

    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if _amounts[i] != 0:
            new_balances[i] -= _amounts[i]
            self._transfer_out(i, _amounts[i], _receiver)

    D1: uint256 = self.get_D_mem(rates, new_balances, amp)
    base_fee: uint256 = unsafe_div(
        unsafe_mul(self.fee, N_COINS),
        unsafe_mul(4, unsafe_sub(N_COINS, 1))
    )
    ys: uint256 = unsafe_div((D0 + D1), N_COINS)

    fees: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    dynamic_fee: uint256 = 0
    xs: uint256 = 0
    ideal_balance: uint256 = 0
    difference: uint256 = 0
    new_balance: uint256 = 0

    for i in range(N_COINS_128, bound=MAX_COINS_128):

        ideal_balance = D1 * old_balances[i] / D0
        difference = 0
        new_balance = new_balances[i]

        if ideal_balance > new_balance:
            difference = unsafe_sub(ideal_balance, new_balance)
        else:
            difference = unsafe_sub(new_balance, ideal_balance)

        xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
        dynamic_fee = self._dynamic_fee(xs, ys, base_fee)
        fees.append(unsafe_div(dynamic_fee * difference, FEE_DENOMINATOR))

        self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
        new_balances[i] -= fees[i]

    D1 = self.get_D_mem(rates, new_balances, amp)  # dev: reuse D1 for new D.
    self.upkeep_oracles(self._xp_mem(rates, new_balances), amp, D1)

    total_supply: uint256 = self.total_supply
    burn_amount: uint256 = unsafe_div((D0 - D1) * total_supply, D0) + 1
    assert burn_amount > 1  # dev: zero tokens burned
    assert burn_amount <= _max_burn_amount, "Slippage screwed you"

    self._burnFrom(msg.sender, burn_amount)

    log RemoveLiquidityImbalance(
        msg.sender,
        _amounts,
        fees,
        D1,
        total_supply - burn_amount
    )

    return burn_amount


@external
@nonreentrant('lock')
def remove_liquidity(
    _burn_amount: uint256,
    _min_amounts: DynArray[uint256, MAX_COINS],
    _receiver: address = msg.sender,
    _claim_admin_fees: bool = True,
) -> DynArray[uint256, MAX_COINS]:
    """
    @notice Withdraw coins from the pool
    @dev Withdrawal amounts are based on current deposit ratios
    @param _burn_amount Quantity of LP tokens to burn in the withdrawal
    @param _min_amounts Minimum amounts of underlying coins to receive
    @param _receiver Address that receives the withdrawn coins
    @return List of amounts of coins that were withdrawn
    """
    total_supply: uint256 = self.total_supply
    assert _burn_amount > 0  # dev: invalid burn amount
    assert len(_min_amounts) == N_COINS  # dev: invalid array length for _min_amounts

    amounts: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    balances: DynArray[uint256, MAX_COINS] = self._balances()

    value: uint256 = 0
    for i in range(N_COINS_128, bound=MAX_COINS_128):

        value = unsafe_div(balances[i] * _burn_amount, total_supply)
        assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
        amounts.append(value)
        self._transfer_out(i, value, _receiver)

    self._burnFrom(msg.sender, _burn_amount)  # <---- Updates self.total_supply

    # --------------------------- Upkeep D_oracle ----------------------------

    ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
    last_D_packed_current: uint256 = self.last_D_packed
    old_D: uint256 = last_D_packed_current & (2**128 - 1)

    self.last_D_packed = self.pack_2(
        old_D - unsafe_div(old_D * _burn_amount, total_supply),  # new_D = proportionally reduce D.
        self._calc_moving_average(
            last_D_packed_current,
            self.D_ma_time,
            ma_last_time_unpacked[1]
        )
    )

    if ma_last_time_unpacked[1] < block.timestamp:
        ma_last_time_unpacked[1] = block.timestamp
        self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])

    # ------------------------------- Log event ------------------------------

    log RemoveLiquidity(
        msg.sender,
        amounts,
        empty(DynArray[uint256, MAX_COINS]),
        unsafe_sub(total_supply, _burn_amount)
    )

    # ------- Withdraw admin fees if _claim_admin_fees is set to True --------
    if _claim_admin_fees:
        self._withdraw_admin_fees()

    return amounts


@external
@nonreentrant('lock')
def withdraw_admin_fees():
    """
    @notice Claim admin fees. Callable by anyone.
    """
    self._withdraw_admin_fees()


# ------------------------ AMM Internal Functions ----------------------------


@view
@internal
def _dynamic_fee(xpi: uint256, xpj: uint256, _fee: uint256) -> uint256:

    _offpeg_fee_multiplier: uint256 = self.offpeg_fee_multiplier
    if _offpeg_fee_multiplier <= FEE_DENOMINATOR:
        return _fee

    xps2: uint256 = (xpi + xpj) ** 2
    return unsafe_div(
        unsafe_mul(_offpeg_fee_multiplier, _fee),
        unsafe_add(
            unsafe_sub(_offpeg_fee_multiplier, FEE_DENOMINATOR) * 4 * xpi * xpj / xps2,
            FEE_DENOMINATOR
        )
    )


@internal
def __exchange(
    x: uint256,
    _xp: DynArray[uint256, MAX_COINS],
    rates: DynArray[uint256, MAX_COINS],
    i: int128,
    j: int128,
) -> uint256:

    amp: uint256 = self._A()
    D: uint256 = self.get_D(_xp, amp)
    y: uint256 = self.get_y(i, j, x, _xp, amp, D)

    dy: uint256 = _xp[j] - y - 1  # -1 just in case there were some rounding errors
    dy_fee: uint256 = unsafe_div(
        dy * self._dynamic_fee(
            unsafe_div(_xp[i] + x, 2), unsafe_div(_xp[j] + y, 2), self.fee
        ),
        FEE_DENOMINATOR
    )

    # Convert all to real units
    dy = (dy - dy_fee) * PRECISION / rates[j]

    self.admin_balances[j] += unsafe_div(
        unsafe_div(dy_fee * admin_fee, FEE_DENOMINATOR) * PRECISION,
        rates[j]
    )

    # Calculate and store state prices:
    xp: DynArray[uint256, MAX_COINS] = _xp
    xp[i] = x
    xp[j] = y
    # D is not changed because we did not apply a fee
    self.upkeep_oracles(xp, amp, D)

    return dy


@internal
def _exchange(
    sender: address,
    i: int128,
    j: int128,
    _dx: uint256,
    _min_dy: uint256,
    receiver: address,
    expect_optimistic_transfer: bool
) -> uint256:

    assert i != j  # dev: coin index out of range
    assert _dx > 0  # dev: do not exchange 0 coins

    rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
    old_balances: DynArray[uint256, MAX_COINS] = self._balances()
    xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, old_balances)

    # --------------------------- Do Transfer in -----------------------------

    # `dx` is whatever the pool received after ERC20 transfer:
    dx: uint256 = self._transfer_in(
        i,
        _dx,
        sender,
        expect_optimistic_transfer
    )

    # ------------------------------- Exchange -------------------------------

    x: uint256 = xp[i] + unsafe_div(dx * rates[i], PRECISION)
    dy: uint256 = self.__exchange(x, xp, rates, i, j)
    assert dy >= _min_dy, "Exchange resulted in fewer coins than expected"

    # --------------------------- Do Transfer out ----------------------------

    self._transfer_out(j, dy, receiver)

    # ------------------------------------------------------------------------

    log TokenExchange(msg.sender, i, dx, j, dy)

    return dy


@internal
def _withdraw_admin_fees():
    fee_receiver: address = factory.fee_receiver()
    if fee_receiver == empty(address):
        return  # Do nothing.

    admin_balances: DynArray[uint256, MAX_COINS] = self.admin_balances
    for i in range(N_COINS_128, bound=MAX_COINS_128):

        if admin_balances[i] > 0:

            self._transfer_out(i, admin_balances[i], fee_receiver)
            admin_balances[i] = 0

    self.admin_balances = admin_balances


# --------------------------- AMM Math Functions -----------------------------


@view
@internal
def get_y(
    i: int128,
    j: int128,
    x: uint256,
    xp: DynArray[uint256, MAX_COINS],
    _amp: uint256,
    _D: uint256
) -> 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_128  # dev: j above N_COINS

    # should be unreachable, but good for safety
    assert i >= 0
    assert i < N_COINS_128

    amp: uint256 = _amp
    D: uint256 = _D

    S_: uint256 = 0
    _x: uint256 = 0
    y_prev: uint256 = 0
    c: uint256 = D
    Ann: uint256 = amp * N_COINS

    for _i in range(MAX_COINS_128):

        if _i == N_COINS_128:
            break

        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


@pure
@internal
def get_D(_xp: DynArray[uint256, MAX_COINS], _amp: uint256) -> uint256:
    """
    D invariant calculation in non-overflowing integer operations
    iteratively

    A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))

    Converging solution:
    D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
    """
    S: 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
        D_P /= pow_mod256(N_COINS, N_COINS)
        Dprev: uint256 = D

        # (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
        D = (
            (unsafe_div(Ann * S, A_PRECISION) + D_P * N_COINS) * D
            /
            (
                unsafe_div((Ann - A_PRECISION) * D, A_PRECISION) +
                unsafe_add(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


@pure
@internal
def get_y_D(
    A: uint256,
    i: int128,
    xp: DynArray[uint256, MAX_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_128  # dev: i above N_COINS

    S_: uint256 = 0
    _x: uint256 = 0
    y_prev: uint256 = 0
    c: uint256 = D
    Ann: uint256 = A * N_COINS

    for _i in range(MAX_COINS_128):

        if _i == N_COINS_128:
            break

        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 _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 + unsafe_sub(A1, A0) * (block.timestamp - t0) / (t1 - t0)
        else:
            return A0 - unsafe_sub(A0, A1) * (block.timestamp - t0) / (t1 - t0)

    else:  # when t1 == 0 or block.timestamp >= t1
        return A1


@pure
@internal
def _xp_mem(
    _rates: DynArray[uint256, MAX_COINS],
    _balances: DynArray[uint256, MAX_COINS]
) -> DynArray[uint256, MAX_COINS]:

    result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    for i in range(N_COINS_128, bound=MAX_COINS_128):
        result.append(unsafe_div(_rates[i] * _balances[i], PRECISION))
    return result


@view
@internal
def get_D_mem(
    _rates: DynArray[uint256, MAX_COINS],
    _balances: DynArray[uint256, MAX_COINS],
    _amp: uint256
) -> uint256:
    xp: DynArray[uint256, MAX_COINS] = self._xp_mem(_rates, _balances)
    return self.get_D(xp, _amp)


@view
@internal
def _calc_withdraw_one_coin(
    _burn_amount: uint256,
    i: int128
) -> (
    uint256,
    uint256,
    DynArray[uint256, MAX_COINS],
    uint256,
    uint256
):
    # First, need to calculate
    # * Get current D
    # * Solve Eqn against y_i for D - _token_amount
    amp: uint256 = self._A()
    rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
    xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, self._balances())
    D0: uint256 = self.get_D(xp, amp)

    total_supply: uint256 = self.total_supply
    D1: uint256 = D0 - _burn_amount * D0 / total_supply
    new_y: uint256 = self.get_y_D(amp, i, xp, D1)

    base_fee: uint256 = unsafe_div(
        unsafe_mul(self.fee, N_COINS),
        unsafe_mul(4, unsafe_sub(N_COINS, 1))
    )
    xp_reduced: DynArray[uint256, MAX_COINS] = xp
    ys: uint256 = unsafe_div((D0 + D1), unsafe_mul(2, N_COINS))

    dx_expected: uint256 = 0
    xp_j: uint256 = 0
    xavg: uint256 = 0
    dynamic_fee: uint256 = 0

    for j in range(MAX_COINS_128):

        if j == N_COINS_128:
            break

        dx_expected = 0
        xp_j = xp[j]

        if j == i:
            dx_expected = xp_j * D1 / D0 - new_y
            xavg = unsafe_div((xp_j + new_y), 2)
        else:
            dx_expected = xp_j - xp_j * D1 / D0
            xavg = xp_j

        dynamic_fee = self._dynamic_fee(xavg, ys, base_fee)
        xp_reduced[j] = xp_j - unsafe_div(dynamic_fee * dx_expected, FEE_DENOMINATOR)

    dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i, xp_reduced, D1)
    dy_0: uint256 = (xp[i] - new_y) * PRECISION / rates[i]  # w/o fees
    dy = unsafe_div((dy - 1) * PRECISION, rates[i])  # Withdraw less to account for rounding errors

    # update xp with new_y for p calculations.
    xp[i] = new_y

    return dy, dy_0 - dy, xp, amp, D1


# -------------------------- AMM Price Methods -------------------------------

@pure
@internal
def pack_2(p1: uint256, p2: uint256) -> uint256:
    assert p1 < 2**128
    assert p2 < 2**128
    return p1 | (p2 << 128)


@pure
@internal
def unpack_2(packed: uint256) -> uint256[2]:
    return [packed & (2**128 - 1), packed >> 128]


@internal
@pure
def _get_p(
    xp: DynArray[uint256, MAX_COINS],
    amp: uint256,
    D: uint256,
) -> DynArray[uint256, MAX_COINS]:

    # dx_0 / dx_1 only, however can have any number of coins in pool
    ANN: uint256 = unsafe_mul(amp, N_COINS)
    Dr: uint256 = unsafe_div(D, pow_mod256(N_COINS, N_COINS))

    for i in range(N_COINS_128, bound=MAX_COINS_128):
        Dr = Dr * D / xp[i]

    p: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    xp0_A: uint256 = unsafe_div(ANN * xp[0], A_PRECISION)

    for i in range(1, MAX_COINS):

        if i == N_COINS:
            break

        p.append(10**18 * (xp0_A + unsafe_div(Dr * xp[0], xp[i])) / (xp0_A + Dr))

    return p


@internal
def upkeep_oracles(xp: DynArray[uint256, MAX_COINS], amp: uint256, D: uint256):
    """
    @notice Upkeeps price and D oracles.
    """
    ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
    last_prices_packed_current: DynArray[uint256, MAX_COINS] = self.last_prices_packed
    last_prices_packed_new: DynArray[uint256, MAX_COINS] = last_prices_packed_current

    spot_price: DynArray[uint256, MAX_COINS] = self._get_p(xp, amp, D)

    # -------------------------- Upkeep price oracle -------------------------

    for i in range(MAX_COINS):

        if i == N_COINS - 1:
            break

        if spot_price[i] != 0:

            # Update packed prices -----------------
            last_prices_packed_new[i] = self.pack_2(
                min(spot_price[i], 2 * 10**18),  # <----- Cap spot value by 2.
                self._calc_moving_average(
                    last_prices_packed_current[i],
                    self.ma_exp_time,
                    ma_last_time_unpacked[0],  # index 0 is ma_last_time for prices
                )
            )

    self.last_prices_packed = last_prices_packed_new

    # ---------------------------- Upkeep D oracle ---------------------------

    last_D_packed_current: uint256 = self.last_D_packed
    self.last_D_packed = self.pack_2(
        D,
        self._calc_moving_average(
            last_D_packed_current,
            self.D_ma_time,
            ma_last_time_unpacked[1],  # index 1 is ma_last_time for D
        )
    )

    # Housekeeping: Update ma_last_time for p and D oracles ------------------
    for i in range(2):
        if ma_last_time_unpacked[i] < block.timestamp:
            ma_last_time_unpacked[i] = block.timestamp

    self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])


@internal
@view
def _calc_moving_average(
    packed_value: uint256,
    averaging_window: uint256,
    ma_last_time: uint256
) -> uint256:

    last_spot_value: uint256 = packed_value & (2**128 - 1)
    last_ema_value: uint256 = (packed_value >> 128)

    if ma_last_time < block.timestamp:  # calculate new_ema_value and return that.
        alpha: uint256 = self.exp(
            -convert(
                unsafe_div(unsafe_mul(unsafe_sub(block.timestamp, ma_last_time), 10**18), averaging_window), int256
            )
        )
        return unsafe_div(last_spot_value * (10**18 - alpha) + last_ema_value * alpha, 10**18)

    return last_ema_value


@view
@external
def last_price(i: uint256) -> uint256:
    return self.last_prices_packed[i] & (2**128 - 1)


@view
@external
def ema_price(i: uint256) -> uint256:
    return (self.last_prices_packed[i] >> 128)


@external
@view
def get_p(i: uint256) -> uint256:
    """
    @notice Returns the AMM State price of token
    @dev if i = 0, it will return the state price of coin[1].
    @param i index of state price (0 for coin[1], 1 for coin[2], ...)
    @return uint256 The state price quoted by the AMM for coin[i+1]
    """
    amp: uint256 = self._A()
    xp: DynArray[uint256, MAX_COINS] = self._xp_mem(
        self._stored_rates(), self._balances()
    )
    D: uint256 = self.get_D(xp, amp)
    return self._get_p(xp, amp, D)[i]


@external
@view
@nonreentrant('lock')
def price_oracle(i: uint256) -> uint256:
    return self._calc_moving_average(
        self.last_prices_packed[i],
        self.ma_exp_time,
        self.ma_last_time & (2**128 - 1)
    )


@external
@view
@nonreentrant('lock')
def D_oracle() -> uint256:
    return self._calc_moving_average(
        self.last_D_packed,
        self.D_ma_time,
        self.ma_last_time >> 128
    )


# ----------------------------- Math Utils -----------------------------------


@internal
@pure
def exp(x: int256) -> uint256:
    """
    @dev Calculates the natural exponential function of a signed integer with
         a precision of 1e18.
    @notice Note that this function consumes about 810 gas units. The implementation
            is inspired by Remco Bloemen's implementation under the MIT license here:
            https://xn--2-umb.com/22/exp-ln.
    @dev This implementation is derived from Snekmate, which is authored
         by pcaversaccio (Snekmate), distributed under the AGPL-3.0 license.
         https://github.com/pcaversaccio/snekmate
    @param x The 32-byte variable.
    @return int256 The 32-byte calculation result.
    """
    value: int256 = x

    # If the result is `< 0.5`, we return zero. This happens when we have the following:
    # "x <= floor(log(0.5e18) * 1e18) ~ -42e18".
    if (x <= -41446531673892822313):
        return empty(uint256)

    # When the result is "> (2 ** 255 - 1) / 1e18" we cannot represent it as a signed integer.
    # This happens when "x >= floor(log((2 ** 255 - 1) / 1e18) * 1e18) ~ 135".
    assert x < 135305999368893231589, "wad_exp overflow"

    # `x` is now in the range "(-42, 136) * 1e18". Convert to "(-42, 136) * 2 ** 96" for higher
    # intermediate precision and a binary base. This base conversion is a multiplication with
    # "1e18 / 2 ** 96 = 5 ** 18 / 2 ** 78".
    value = unsafe_div(x << 78, 5 ** 18)

    # Reduce the range of `x` to "(-½ ln 2, ½ ln 2) * 2 ** 96" by factoring out powers of two
    # so that "exp(x) = exp(x') * 2 ** k", where `k` is a signer integer. Solving this gives
    # "k = round(x / log(2))" and "x' = x - k * log(2)". Thus, `k` is in the range "[-61, 195]".
    k: int256 = unsafe_add(unsafe_div(value << 96, 54916777467707473351141471128), 2 ** 95) >> 96
    value = unsafe_sub(value, unsafe_mul(k, 54916777467707473351141471128))

    # Evaluate using a "(6, 7)"-term rational approximation. Since `p` is monic,
    # we will multiply by a scaling factor later.
    y: int256 = unsafe_add(unsafe_mul(unsafe_add(value, 1346386616545796478920950773328), value) >> 96, 57155421227552351082224309758442)
    p: int256 = unsafe_add(unsafe_mul(unsafe_add(unsafe_mul(unsafe_sub(unsafe_add(y, value), 94201549194550492254356042504812), y) >> 96,\
                           28719021644029726153956944680412240), value), 4385272521454847904659076985693276 << 96)

    # We leave `p` in the "2 ** 192" base so that we do not have to scale it up
    # again for the division.
    q: int256 = unsafe_add(unsafe_mul(unsafe_sub(value, 2855989394907223263936484059900), value) >> 96, 50020603652535783019961831881945)
    q = unsafe_sub(unsafe_mul(q, value) >> 96, 533845033583426703283633433725380)
    q = unsafe_add(unsafe_mul(q, value) >> 96, 3604857256930695427073651918091429)
    q = unsafe_sub(unsafe_mul(q, value) >> 96, 14423608567350463180887372962807573)
    q = unsafe_add(unsafe_mul(q, value) >> 96, 26449188498355588339934803723976023)

    # The polynomial `q` has no zeros in the range because all its roots are complex.
    # No scaling is required, as `p` is already "2 ** 96" too large. Also,
    # `r` is in the range "(0.09, 0.2...

// [truncated — 62012 bytes total]

Read Contract

A 0xf446c1d0 → uint256
A_precise 0x76a2f0f0 → uint256
DOMAIN_SEPARATOR 0x3644e515 → bytes32
D_ma_time 0x9c4258c4 → uint256
D_oracle 0x907a016b → uint256
N_COINS 0x29357750 → uint256
admin_balances 0xe2e7d264 → uint256
admin_fee 0xfee3f7f9 → uint256
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
balances 0x4903b0d1 → uint256
calc_token_amount 0x3db06dd8 → uint256
calc_withdraw_one_coin 0xcc2b27d7 → uint256
coins 0xc6610657 → address
decimals 0x313ce567 → uint8
dynamic_fee 0x76a9cd3e → uint256
ema_price 0x90d20837 → uint256
fee 0xddca3f43 → uint256
future_A 0xb4b577ad → uint256
future_A_time 0x14052288 → uint256
get_balances 0x14f05979 → uint256[]
get_dx 0x67df02ca → uint256
get_dy 0x5e0d443f → uint256
get_p 0xec023862 → uint256
get_virtual_price 0xbb7b8b80 → uint256
initial_A 0x5409491a → uint256
initial_A_time 0x2081066c → uint256
last_price 0x3931ab52 → uint256
ma_exp_time 0x1be913a5 → uint256
ma_last_time 0x1ddc3b01 → uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
offpeg_fee_multiplier 0x8edfdd5f → uint256
price_oracle 0x68727653 → uint256
salt 0xbfa0b133 → bytes32
stored_rates 0xfd0684b1 → uint256[]
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
version 0x54fd4d50 → string

Write Contract 22 functions

These functions modify contract state and require a wallet transaction to execute.

add_liquidity 0xb72df5de
uint256[] _amounts
uint256 _min_mint_amount
returns: uint256
add_liquidity 0xa7256d09
uint256[] _amounts
uint256 _min_mint_amount
address _receiver
returns: uint256
approve 0x095ea7b3
address _spender
uint256 _value
returns: bool
exchange 0x3df02124
int128 i
int128 j
uint256 _dx
uint256 _min_dy
returns: uint256
exchange 0xddc1f59d
int128 i
int128 j
uint256 _dx
uint256 _min_dy
address _receiver
returns: uint256
exchange_received 0x7e3db030
int128 i
int128 j
uint256 _dx
uint256 _min_dy
returns: uint256
exchange_received 0xafb43012
int128 i
int128 j
uint256 _dx
uint256 _min_dy
address _receiver
returns: uint256
permit 0xd505accf
address _owner
address _spender
uint256 _value
uint256 _deadline
uint8 _v
bytes32 _r
bytes32 _s
returns: bool
ramp_A 0x3c157e64
uint256 _future_A
uint256 _future_time
remove_liquidity 0xd40ddb8c
uint256 _burn_amount
uint256[] _min_amounts
returns: uint256[]
remove_liquidity 0x5e604cd2
uint256 _burn_amount
uint256[] _min_amounts
address _receiver
returns: uint256[]
remove_liquidity 0x2969e04a
uint256 _burn_amount
uint256[] _min_amounts
address _receiver
bool _claim_admin_fees
returns: uint256[]
remove_liquidity_imbalance 0x7706db75
uint256[] _amounts
uint256 _max_burn_amount
returns: uint256
remove_liquidity_imbalance 0x4a6e32c6
uint256[] _amounts
uint256 _max_burn_amount
address _receiver
returns: uint256
remove_liquidity_one_coin 0x1a4d01d2
uint256 _burn_amount
int128 i
uint256 _min_received
returns: uint256
remove_liquidity_one_coin 0x081579a5
uint256 _burn_amount
int128 i
uint256 _min_received
address _receiver
returns: uint256
set_ma_exp_time 0x65bbea6b
uint256 _ma_exp_time
uint256 _D_ma_time
set_new_fee 0x015c2838
uint256 _new_fee
uint256 _new_offpeg_fee_multiplier
stop_ramp_A 0x551a6588
No parameters
transfer 0xa9059cbb
address _to
uint256 _value
returns: bool
transferFrom 0x23b872dd
address _from
address _to
uint256 _value
returns: bool
withdraw_admin_fees 0x30c54085
No parameters

Recent Transactions

No transactions found for this address