Address Contract Partially Verified
Address
0x66da369fC5dBBa0774Da70546Bd20F2B242Cd34d
Balance
0 ETH
Nonce
1
Code Size
21585 bytes
Creator
0x0c0e5f2f...4963 at tx 0xcf0dee94...f515d1
Indexed Transactions
0
Contract Bytecode
21585 bytes
0x60003560e01c6002603f820660011b6151b301601e39600051565b63ed6c1546811861291b57346151ae57602061529160403960206040f361291b565b63c66106578118610071576024361034176151ae576020600435600281116151ae5760051b6080016152310160403960206040f35b63556d6e9f811861291b576064361034176151ae57602061531160003960005163e31593d8606052602060606004607c845afa6100b3573d600060003e3d6000fd5b60203d106151ae576060518060a01c6151ae5760a05260a09050516040526020604051633bb1f8c1606052606060046080373060e052602060606084607c845afa610103573d600060003e3d6000fd5b60203d106151ae5760609050f361291b565b63c45a0155811861013357346151ae57602061531160403960206040f35b637ecebe00811460033611161561291b576024361034176151ae576004358060a01c6151ae57604052601b60405160205260005260406000205460605260206060f361291b565b634d23bfa0811861291b57346151ae5760055460405260206040f361291b565b63175753e981186101b657346151ae5760065460405260206040f35b63dd62ed3e811861291b576044361034176151ae576004358060a01c6151ae576040526024358060a01c6151ae576060526019604051602052600052604060002080606051602052600052604060002090505460805260206080f361291b565b6399f6bdda811861023257346151ae5760075460405260206040f35b63204fe3d5811861024e57346151ae5760085460405260206040f35b634515cef3811861291b576084361034176151ae57336106205261080a5661291b565b63e89876ff811861028d57346151ae5760095460405260206040f35b635b41b90881186102ac576084361034176151ae57336109a052611363565b63a9059cbb811861291b576044361034176151ae576004358060a01c6151ae5760c0523360405260c0516060526024356080526102e761507d565b600160e052602060e0f361291b565b63f30cfad5811861291b57346151ae57600a5460405260206040f361291b565b63f9ed9597811861033257346151ae57600b5460405260206040f35b633644e515811861291b57346151ae57602061034f610120615119565b610120f361291b565b634903b0d18118610385576024361034176151ae57600435600281116151ae57600c015460405260206040f35b6368727653811861291b576024361034176151ae576000546002146151ae576002546040526103b5610100612d4f565b610100600435600181116151ae5760051b810190505160e0526001546040526103df610120612d4f565b610120600435600181116151ae5760051b81019050516101005260055460405261040a610140612eaa565b6101405161012052426101205110156105255760045460405261042e610160612d4f565b610160600435600181116151ae5760051b810190505161014052601354604052610459610180612921565b610180604081019050516101605261012051604052610160516060526104806101a0612ecf565b6101a0516101805261014051610100518060011b818160011c186151ae579050808281188284100218905090506101805180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae579050905060e051610180518082028115838383041417156151ae57905090508082018281106151ae5790509050670de0b6b3a7640000810490506101a05260206101a061052a565b602060e05bf361291b565b630f529ba2811861054c57346151ae57600f5460405260206040f35b6372d4f0e2811861057857346151ae57602060145460405261056e6060612921565b6060604081019050f35b63244c7c2e811861291b57346151ae57602061531160003960005163f851a440610120526020610120600461013c845afa6105b8573d600060003e3d6000fd5b60203d106151ae57610120518060a01c6151ae576101605261016090505133186151ae576105e7610160612bf6565b610160805161012052602081015161014052506101205160801b61016052610140516101605117610160526101605160085561016051600a554260095542600b557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101205161018052610140516101a052426101c0526060610180a10061291b565b637ba1a74d811861068657346151ae5760105460405260206040f35b63e361640581186106a257346151ae5760145460405260206040f35b6323c6afea811861291b57346151ae576000546002146151ae576005546040526106cc60e0612eaa565b60e06020810190505160c05260035460e0524260c05110156107825760c0516040526007546060526106ff610120612ecf565b61012051610100526006546101005180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae579050905060e051610100518082028115838383041417156151ae57905090508082018281106151ae5790509050670de0b6b3a764000081049050610120526020610120610787565b602060e05bf361291b565b630b7b594b81186107a957346151ae5760115460405260206040f35b630c46b72a811861291b57346151ae5760125460405260206040f361291b565b633dd6547881186107e557346151ae5760135460405260206040f35b6375b96abc811861291b5760a4361034176151ae576084358060a01c6151ae57610620525b6000546002146151ae576002600055610824610680612bf6565b61068080516106405260208101516106605250600c5461068052600d546106a052600e546106c05260c0366106e0376004356024358082018281106151ae57905090506044358082018281106151ae5790509050156151ae576001546107a0526107a051604052610896610800612d4f565b61080080516107c05260208101516107e0525061068051610800526106a051610820526106c051610840526060366108603760006003905b806108c0526108c051600281116151ae5760051b600401351561097f576108c0516040526108c051600281116151ae5760051b6004013560605233608052600060a05261091c6108e061296c565b6108e0516108c051600281116151ae5760051b61086001526108c051600281116151ae5760051b61068001516108c051600281116151ae5760051b61086001518082018281106151ae57905090506108c051600281116151ae5760051b61068001525b6001018181186108ce5750506106805160206152316000396000518082028115838383041417156151ae5790509050610680526108005160206152316000396000518082028115838383041417156151ae57905090506108005260006003905b806108c05260016108c05110610b0957670de0b6b3a76400006108c051600281116151ae5760051b61068001516108c051600181038181116151ae579050600181116151ae5760051b6107c001518082028115838383041417156151ae579050905060206108c051600281116151ae5760051b615231016000396000518082028115838383041417156151ae5790509050046108c051600281116151ae5760051b6106800152670de0b6b3a76400006108c051600281116151ae5760051b610800015160206108c051600281116151ae5760051b615231016000396000516108c051600181038181116151ae579050600181116151ae5760051b6107c00151028082028115838383041417156151ae5790509050046108c051600281116151ae5760051b61080001525b6108c051600281116151ae5760051b610860015115610b6d576108c051600281116151ae5760051b61068001516108c051600281116151ae5760051b61080001518082038281116151ae57905090506108c051600281116151ae5760051b6106e001525b6001018181186109df57505042600b5411610b8e57600f5461078052610bff565b6020615291600039600051637b12e0096108c052610640516108e052610660516109005261080051610920526108205161094052610840516109605260006109805260206108c060c46108dc845afa610bec573d600060003e3d6000fd5b60203d106151ae576108c0905051610780525b6020615291600039600051637b12e0096108e0526106405161090052610660516109205261068051610940526106a051610960526106c0516109805260006109a05260206108e060c46108fc845afa610c5d573d600060003e3d6000fd5b60203d106151ae576108e09050516108c052601a546108e0526107805115610cc7576108e0516108c0518082028115838383041417156151ae57905090506107805180156151ae57808204905090506108e0518082038281116151ae579050905061074052610ce9565b6108c0516040526107a051606052610ce06109006142f0565b61090051610740525b61074051156151ae576107805115610e41576106e0516101e0526107005161020052610720516102205261068051610240526106a051610260526106c05161028052610d366109006143e8565b61090051610740518082028115838383041417156151ae57905090506402540be40081049050600181018181106151ae5790506107605261074051610760518082038281116151ae5790509050610740526108e051610740518082018281106151ae57905090506108e0526106205160405261074051606052610dba610900614517565b610900506017546402540be400601554610760518082028115838383041417156151ae5790509050048082018281106151ae57905090506017556106405160e052610660516101005261068051610120526106a051610140526106c051610160526108c0516101805260006101a052610e34610900612fea565b610900516107a052610e91565b6108c051600f55670de0b6b3a7640000601255670de0b6b3a7640000601055670de0b6b3a7640000601155610740516003556106205160405261074051606052610e8c610900614517565b610900505b606435610740511015610f04576008610900527f536c6970706167650000000000000000000000000000000000000000000000006109205261090050610900518061092001601f826000031636823750506308c379a06108c05260206108e052601f19601f6109005101166044016108dcfd5b610620517fe1b60455bd9e33720b547f60e4e0cfbf1252d0f2ee0147d53029945f39fe3c1a610860516109005261088051610920526108a0516109405261076051610960526108e051610980526107a0516109a05260c0610900a260206107406003600055f361291b565b63fee3f7f9811861291b57346151ae5760155460405260206040f361291b565b6306fdde03811861291b57346151ae57602080604052806040016020602061533160003960005101806153318339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f361291b565b6395d89b41811861291b57346151ae57602080604052806040016020602061539160003960005101806153918339508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f361291b565b63313ce567811861106457346151ae57601260405260206040f35b6392526c0c811861108a57346151ae5760206014546040526110866060612921565b6060f35b6309c3da6a811861291b57346151ae576013546040526110aa6060612921565b6060604081019050516102b68102816102b68204186151ae5790506103e88104905060c052602060c0f361291b565b6354fd4d50811861291b57346151ae5760208060805260066040527f76322e302e31000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f361291b565b6370a08231811861291b576024361034176151ae576004358060a01c6151ae57604052601860405160205260005260406000205460605260206060f361291b565b6318160ddd811861291b57346151ae57601a5460405260206040f361291b565b63bfa0b13381186111db57346151ae57602061541160403960206040f35b63767691e781186112f35760a4361034176151ae576084358060a01c6151ae576109a0525b6000546002146151ae57600260005560043560405260443560605233608052600160a05261122f6109e061296c565b6109e0516109c05260406004610620376109c0516106605260643561068052611259610a40613c94565b610a4080516109e0526020810151610a00526040810151610a2052506024356040526109e0516060526109a051608052611291612b49565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610a40526109c051610a6052602435610a80526109e051610aa052610a0051610ac052610a2051610ae05260c0610a40a260206109e06003600055f35b63a3f7cdd5811861291b576024361034176151ae576000546002146151ae57602060015460405261132460e0612d4f565b60e0600435600181116151ae5760051b81019050f361291b565b63a64833a081186114565760a4361034176151ae576084358060a01c6151ae576109a0525b6000546002146151ae57600260005560043560405260443560605233608052600060a0526113926109e061296c565b6109e0516109c05260406004610620376109c05161066052606435610680526113bc610a40613c94565b610a4080516109e0526020810151610a00526040810151610a2052506024356040526109e0516060526109a0516080526113f4612b49565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c600435610a40526109c051610a6052602435610a80526109e051610aa052610a0051610ac052610a2051610ae05260c0610a40a260206109e06003600055f35b63f1dc3cc9811861291b576064361034176151ae5733610620526114e05661291b565b6329b244bb811861291b576084361034176151ae57336109a0526112005661291b565b63ecb586a581186114bb576084361034176151ae5733610180526117a4565b630fbcee6e81186116c5576084361034176151ae576064358060a01c6151ae57610620525b6000546002146151ae5760026000556114f7614711565b611502610680612bf6565b6106808051610640526020810151610660525060e03661068037610640516101e0526106605161020052604060046102203742600b541161026052611548610760614b07565b61076080516106805260208101516106a0526040810180516106e0526020810151610700526040810151610720525060a081015161074052506044356106805110156115f4576008610760527f536c6970706167650000000000000000000000000000000000000000000000006107805261076050610760518061078001601f826000031636823750506308c379a061072052602061074052601f19601f61076051011660440161073cfd5b3360405260043560605261160961076061458c565b610760506106405160e05261066051610100526106e05161012052610700516101405261072051610160526106a0516101805260006101a05261164d610780612fea565b61078051610760526024356040526106805160605261062051608052611671612b49565b337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c6040600461078037610680516107c052610740516107e052610760516108005260a0610780a260206106806003600055f35b633883e119811861291b576084361034176151ae576064358060011c6151ae57604052602061531160003960005163e31593d8608052602060806004609c845afa611715573d600060003e3d6000fd5b60203d106151ae576080518060a01c6151ae5760c05260c09050516060526020606051638585c4b16080526060600460a0376040516101005230610120526020608060a4609c845afa61176d573d600060003e3d6000fd5b60203d106151ae5760809050f361291b565b632da5dc218118611b625760a4361034176151ae576084358060a01c6151ae57610180525b6000546002146151ae5760026000556004356101a052600c546101c052600d546101e052600e546102005260603661022037601a5461028052336040526004356060526117f26102a061458c565b6102a050610280516101a051186118475760006003905b806102a0526102a051600281116151ae5760051b6101c001516102a051600281116151ae5760051b61022001526001018181186118095750506118f1565b6101a051600181038181116151ae5790506101a05260006003905b806102a0526102a051600281116151ae5760051b6101c001516101a0518082028115838383041417156151ae57905090506102805180156151ae57808204905090506102a051600281116151ae5760051b61022001526102a051600281116151ae5760051b602401356102a051600281116151ae5760051b6102200151106151ae576001018181186118625750505b600f546102a0526102a051610280516102a0516101a0518082028115838383041417156151ae5790509050048082038281116151ae5790509050600f5560006003905b806102c0526102c0516040526102c051600281116151ae5760051b610220015160605261018051608052611966612b49565b600101818118611934575050337fd6cc314a0b1e3b2579f8e64248e82434072e8271290eef8ad0886709304195f5610220516102c052610240516102e0526102605161030052610280516004358082038281116151ae57905090506103205260806102c0a2600c54604052600d54606052600e5460805260015460a0526119ee610320614601565b61032080516102c05260208101516102e05260408101516103005250602061529160003960005163bad1dc26610340526102c051610360526102e05161038052610300516103a0526020610340606461035c845afa611a52573d600060003e3d6000fd5b60203d106151ae5761034090505161032052600554604052611a75610380612eaa565b6103808051610340526020810151610360525042610360511015611b4f576003546103805261036051604052600754606052611ab26103c0612ecf565b6103c0516103a052670de0b6b3a7640000610320516103a05180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae5790509050610380516103a0518082028115838383041417156151ae57905090508082018281106151ae57905090500460035542610360526103405160405261036051606052611b476103c061295d565b6103c0516005555b6103205160065560606102206003600055f35b63572e5625811861291b576064361034176151ae57602060606004606037611b8b6101e0612db1565b6101e0f361291b565b6323b872dd8118611c64576064361034176151ae576004358060a01c6151ae5760c0526024358060a01c6151ae5760e052601960c051602052600052604060002080336020526000526040600020905054610100527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101005114611c3d5760c05160405233606052610100516044358082038281116151ae5790509050608052611c3d615024565b60c05160405260e051606052604435608052611c5761507d565b6001610120526020610120f35b6375f66026811861291b5760e4361034176151ae576000546002146151ae576002600055602061531160003960005163f851a44060a052602060a0600460bc845afa611cb5573d600060003e3d6000fd5b60203d106151ae5760a0518060a01c6151ae5760e05260e090505133186151ae576060600460a037601454604052611cee610160612921565b610160805161010052602081015161012052604081015161014052506402540be40060c0511115611d25576101205160c052611d32565b6207a12060c051106151ae575b6402540be40160a05110611d48576101005160a0525b60c05160a051116151ae57670de0b6b3a763ffff60e0511115611d71576101405160e052611d7a565b60e051156151ae575b60a05160405260c05160605260e051608052611d9761016061500e565b610160516014556060606461016037601354604052611db7610220612921565b61022080516101c05260208101516101e05260408101516102005250670de0b6b3a76400016101605110611dee576101c051610160525b670de0b6b3a76400016101805110611e09576101e051610180525b620d505d6101a0511115611e2457610200516101a052611e30565b60576101a051106151ae575b61016051604052610180516060526101a051608052611e5061022061500e565b6102205160135560c43561022052620d505d610220511115611e785760075461022052611e84565b605761022051106151ae575b610220516007557f1c65bbdc939f346e5d6f0bde1f072819947438d4fc7b182cc59c2f6dc550408760a0516102405260c0516102605260e05161028052610160516102a052610180516102c0526101a0516102e05260c4356103005260e0610240a160036000550061291b565b63095ea7b3811861291b576044361034176151ae576004358060a01c6151ae5760c0523360405260c051606052602435608052611f2c615024565b600160e052602060e0f361291b565b63d505accf811861291b5760e4361034176151ae576004358060a01c6151ae57610120526024358060a01c6151ae57610140526084358060081c6151ae576101605261012051156151ae5760643542116151ae57601b6101205160205260005260406000205461018052600060026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c0805160208201836103200181518152505080830192505050611ff8610200615119565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120516000610240526101a0516101c052610160516101e052604060a461020037602061024060806101c060015afa5061024051186151ae5760016101805101601b6101205160205260005260406000205561012051604052610140516060526044356080526120f7615024565b60016101c05260206101c0f361291b565b63cab4d3db811861291b57346151ae576020602061531160003960005163cab4d3db604052602060406004605c845afa612147573d600060003e3d6000fd5b60203d106151ae576040518060a01c6151ae5760805260809050f361291b565b63f851a440811861291b57346151ae576020602061531160003960005163f851a440604052602060406004605c845afa6121a6573d600060003e3d6000fd5b60203d106151ae576040518060a01c6151ae5760805260809050f361291b565b6337ed3a7a811861291b576064361034176151ae57602061531160003960005163e31593d8606052602060606004607c845afa612208573d600060003e3d6000fd5b60203d106151ae576060518060a01c6151ae5760a05260a090505160405260206040516399bf0b76606052606060046080373060e052602060606084607c845afa612258573d600060003e3d6000fd5b60203d106151ae5760609050f361291b565b6354f0f7d5811861234557346151ae576000546002146151ae57600254604052612295610120612d4f565b610120805160e05260208101516101005250601254600381028160038204186151ae579050602061529160003960005163f42c56c26101205260e051610100518082028115838383041417156151ae5790509050610140526020610120602461013c845afa612309573d600060003e3d6000fd5b60203d106151ae576101209050518082028115838383041417156151ae579050905069d3c21bcecceda100000081049050610160526020610160f35b63cde699fa811861291b5760c4361034176151ae576020606060046101e03760606064610240376123776103406143e8565b610340f361291b565b63bb7b8b80811861291b57346151ae576000546002146151ae57600f546040526001546060526123b16101806142f0565b61018051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae579050601a5480156151ae57808204905090506101a05260206101a0f361291b565b6359189017811861291b576024361034176151ae57602060045460405261241960e0612d4f565b60e0600435600181116151ae5760051b81019050f361291b565b63ddca3f43811861291b57346151ae576020600c54604052600d54606052600e5460805260015460a0526124686101e0614601565b6101e080516102605260208101516102805260408101516102a0525061026051606052610280516080526102a05160a0526124a4610240612db1565b610240f361291b565b634fb08c5e811861291b576044361034176151ae5760206124cf610600612bf6565b61060080516107005260208101516107205250604060046107403742600b541161078052610700516101e0526107205161020052610740516102205261076051610240526107805161026052612526610640614b07565b610640f361291b565b63f446c1d0811861291b57346151ae57602061254c610120612bf6565b610120f361291b565b63b1373929811861257d57346151ae576020612572610120612bf6565b610120602081019050f35b63ee8de675811861291b57346151ae57602060145460405261259f6060612921565b6060602081019050f361291b565b6349fe9e77811861291b57346151ae5760206013546040526125cf6060612921565b6060f361291b565b63083812e5811861291b57346151ae5760206013546040526125f96060612921565b6060602081019050f361291b565b633620604b811861291b57346151ae57606061523160403960606040f361291b565b635e248072811861291b576064361034176151ae57602061531160003960005163f851a440610120526020610120600461013c845afa61266e573d600060003e3d6000fd5b60203d106151ae57610120518060a01c6151ae576101605261016090505133186151ae576009546201517f81018181106151ae5790504211156151ae57426201518081018181106151ae579050600181038181116151ae57905060443511156151ae576126dc610160612bf6565b610160805161012052602081015161014052506101205160801b6101605261014051610160511761016052610a8c600435106151ae57631017df80600435116151ae576402540be400602435106151ae5766b1a2bc2ec50000602435116151ae57600435670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506101205180156151ae578082049050905061018052678ac7230489e8000061018051116151ae5767016345785d8a000061018051106151ae57602435670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506101405180156151ae578082049050905061018052678ac7230489e8000061018051116151ae5767016345785d8a000061018051106151ae57610160516008554260095560043560801b6101a0526024356101a051176101a052604435600b556101a051600a557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd05610120516101c0526004356101e05261014051610200526024356102205242610240526044356102605260c06101c0a10061291b565b633217902f811861291b576024361034176151ae57602061531160003960005163f851a440604052602060406004605c845afa6128b8573d600060003e3d6000fd5b60203d106151ae576040518060a01c6151ae57608052608090505133186151ae576402540be400600435116151ae576004356015557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b7460043560405260206040a1005b60006000fd5b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b60605160801b60405117815250565b6020604051600281116151ae5760051b608001615231016000396000516370a0823160e0523061010052602060e0602460fc845afa6129b0573d600060003e3d6000fd5b60203d106151ae5760e090505160c05260a05115612a255760c051604051600281116151ae57600c01548082038281116151ae579050905060e05260605160e051106151ae57604051600281116151ae57600c01805460e0518082018281106151ae579050905081555060e051815250612b47565b6020604051600281116151ae5760051b608001615231016000396000516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc6000855af1612a79573d600060003e3d6000fd5b3d612a9057803b156151ae57600161016052612aa8565b60203d106151ae5760e0518060011c6151ae57610160525b610160905051156151ae576020604051600281116151ae5760051b608001615231016000396000516370a082316101005230610120526020610100602461011c845afa612afa573d600060003e3d6000fd5b60203d106151ae5761010090505160c0518082038281116151ae579050905060e052604051600281116151ae57600c01805460e0518082018281106151ae579050905081555060e0518152505b565b604051600281116151ae57600c0180546060518082038281116151ae57905090508155506020604051600281116151ae5760051b6080016152310160003960005163a9059cbb60a05260805160c05260605160e052602060a0604460bc6000855af1612bba573d600060003e3d6000fd5b3d612bd157803b156151ae57600161010052612be9565b60203d106151ae5760a0518060011c6151ae57610100525b610100905051156151ae57565b600b54604052600a546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a052604051421015612d3f5760085460c05260095460e05260405160e0518082038281116151ae57905090506040524260e0518082038281116151ae579050905060e05260405160e0518082038281116151ae57905090506101005260c05160801c610100518082028115838383041417156151ae579050905060a05160e0518082028115838383041417156151ae57905090508082018281106151ae579050905060405180156151ae578082049050905060a0526fffffffffffffffffffffffffffffffff60c05116610100518082028115838383041417156151ae579050905060805160e0518082028115838383041417156151ae57905090508082018281106151ae579050905060405180156151ae57808204905090506080525b60a0518152608051602082015250565b60403660603760405160a05260006002905b8060c0526fffffffffffffffffffffffffffffffff60a0511660c051600181116151ae5760051b6060015260a05160801c60a052600101818118612d615750506060518152608051602082015250565b601454604052612dc2610120612921565b610120805160c052602081015160e05260408101516101005250602061529160003960005163fa18042d61014052606051610160526080516101805260a0516101a052610100516101c0526020610140608461015c845afa612e29573d600060003e3d6000fd5b60203d106151ae5761014090505161012052670de0b6b3a764000060c051610120518082028115838383041417156151ae579050905060e0516101205180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae57905090508082018281106151ae579050905004815250565b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b60206152916000396000516381d18d87608052606051426040518082038281116151ae5790509050670de0b6b3a7640000810281670de0b6b3a76400008204186151ae579050048060ff1c6151ae577f800000000000000000000000000000000000000000000000000000000000000081146151ae5760000360a052602060806024609c845afa612f65573d600060003e3d6000fd5b60203d106151ae576080905051815250565b60403660803760006002905b8060c05260805160801b60805260c05180600103600181116151ae579050600181116151ae5760051b6040015160a0526ffffffffffffffffffffffffffffffffe60a051116151ae5760805160a05117608052600101818118612f83575050608051815250565b600254604052612ffb610200612d4f565b61020080516101c05260208101516101e0525060045460405261301f610240612d4f565b61024080516102005260208101516102205250600154610240526102405160405261304b6102a0612d4f565b6102a08051610260526020810151610280525060135460405261306f610300612921565b61030080516102a05260208101516102c05260408101516102e05250601a546103005260105461032052601254610340526005546040526130b16103a0612eaa565b6103a08051610360526020810151610380525060006103a0524261036051101561320457610360516040526102e0516060526130ee6103c0612ecf565b6103c0516103a05260006002905b806103c052670de0b6b3a76400006103c051600181116151ae5760051b61020001516103c051600181116151ae5760051b61026001518060011b818160011c186151ae579050808281188284100218905090506103a05180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae57905090506103c051600181116151ae5760051b6101c001516103a0518082028115838383041417156151ae57905090508082018281106151ae5790509050046103c051600181116151ae5760051b6101c001526001018181186130fc5750506101c0516040526101e0516060526131f76103c0612f77565b6103c05160025542610360525b426103805110156132aa576003546103c0526103805160405260075460605261322e6103e0612ecf565b6103e0516103a052670de0b6b3a76400006006546103a05180670de0b6b3a764000003670de0b6b3a764000081116151ae5790508082028115838383041417156151ae57905090506103c0516103a0518082028115838383041417156151ae57905090508082018281106151ae57905090500460035542610380525b61036051604052610380516060526132c36103c061295d565b6103c051600555610180516103c0526101805161334c576020615291600039600051637b12e0096103e05260e0516104005261010051610420526101205161044052610140516104605261016051610480526101a0516104a05260206103e060c46103fc845afa613339573d600060003e3d6000fd5b60203d106151ae576103e09050516103c0525b602061529160003960005163754b76b36103e0526101205161040052610140516104205261016051610440526103c0516104605260e05161048052610100516104a05260406103e060c46103fc845afa6133ab573d600060003e3d6000fd5b60403d106151ae576103e090508051610200526020810151610220525060006002905b806103e052670de0b6b3a76400006103e051600181116151ae5760051b61020001516103e051600181116151ae5760051b61026001518082028115838383041417156151ae5790509050046103e051600181116151ae5760051b61020001526001018181186133ce57505061020051604052610220516060526134526103e0612f77565b6103e0516004556060366103e03760036103c051046103e05260006002905b80610440526103c051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae57905061044051600181116151ae5760051b6102600151600381028160038204186151ae57905080156151ae578082049050905061044051600181018181106151ae579050600281116151ae5760051b6103e00152600101818118613471575050670de0b6b3a764000061044052670de0b6b3a764000061046052610340511561365457602061529160003960005163bad1dc266104a0526103e0516104c052610400516104e052610420516105005260206104a060646104bc845afa613561573d600060003e3d6000fd5b60203d106151ae576104a09050516104805261048051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506103005180156151ae5780820490509050610460526103405161032051610460518082028115838383041417156151ae5790509050046104405242600b54101561364c5761034051610460511161364c5760046104a0527f4c6f7373000000000000000000000000000000000000000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b610480516006555b61044051601055610440516102a0518060011b818160011c186151ae5790508082018281106151ae5790509050610460518060011b818160011c186151ae579050670de0b6b3a764000081038181116151ae5790501115613c7c576040366104803760006002905b806104c0526104c051600181116151ae5760051b61026001516104c051600181116151ae5760051b6101c00151670de0b6b3a7640000810281670de0b6b3a76400008204186151ae579050046104a052670de0b6b3a76400016104a0511015613736576104a051670de0b6b3a7640000036104a052613749565b670de0b6b3a76400006104a051036104a0525b6104a0516fffffffffffffffffffffffffffffffff81116151ae576002810a90506104805101610480526001018181186136bc575050610480518060b57101000000000000000000000000000000000082106137ac578160801c91508060401b90505b690100000000000000000082106137ca578160401c91508060201b90505b6501000000000082106137e4578160201c91508060101b90505b630100000082106137fc578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050610480526102c05160056104805104808281188284110218905090506104c0526104c051610480511115613c7c576040366104e03760006002905b80610520526104805161052051600181116151ae5760051b61026001516104c05161048051038082028115838383041417156151ae57905090506104c05161052051600181116151ae5760051b6101c001518082028115838383041417156151ae57905090508082018281106151ae57905090500461052051600181116151ae5760051b6104e0015260010181811861389e575050610120516103e0526101405161040052610160516104205260006002905b806105205261052051600181116151ae5760051b610260015161052051600181018181106151ae579050600281116151ae5760051b610120015161052051600181116151ae5760051b6104e001518082028115838383041417156151ae57905090500461052051600181018181106151ae579050600281116151ae5760051b6103e001526001018181186139515750506020615291600039600051637b12e0096105405260e0516105605261010051610580526103e0516105a052610400516105c052610420516105e052600061060052602061054060c461055c845afa613a3e573d600060003e3d6000fd5b60203d106151ae576105409050516105205261052051156151ae5760006003905b80610540526105205161054051600281116151ae5760051b6103e00151670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790500461056052662386f26fc10000610560511015613ab8576000613ac9565b68056bc75e2d631000006105605111155b156151ae57600101818118613a5f575050600361052051046103e05260006002905b806105405261052051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae57905061054051600181116151ae5760051b6104e00151600381028160038204186151ae57905080156151ae578082049050905061054051600181018181106151ae579050600281116151ae5760051b6103e00152600101818118613aeb57505061030051602061529160003960005163bad1dc26610540526103e051610560526104005161058052610420516105a0526020610540606461055c845afa613bbc573d600060003e3d6000fd5b60203d106151ae57610540905051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790500461034052670de0b6b3a7640001610340511015613c07576000613c35565b61044051610340518060011b818160011c186151ae579050670de0b6b3a764000081038181116151ae579050115b15613c7c576104e05160405261050051606052613c53610540612f77565b610540516102405261052051600f55610340516012556102405160015561024051815250613c92565b6103c051600f5561046051601255610240518152505b565b6106405161062051146151ae5761066051156151ae57613cb56106e0612bf6565b6106e080516106a05260208101516106c05250600c546106e052600d5461070052600e546107205260006107405261064051600281116151ae5760051b6106e001516107605261062051600281116151ae5760051b6106e00151610660518082038281116151ae5790509050610780526001546107a0526107a051604052613d3e610800612d4f565b61080080516107c05260208101516107e052506106e05160206152316000396000518082028115838383041417156151ae57905090506106e052600160028101905b8061080052670de0b6b3a764000061080051600281116151ae5760051b6106e0015161080051600181038181116151ae579050600181116151ae5760051b6107c001518082028115838383041417156151ae5790509050602061080051600281116151ae5760051b615231016000396000518082028115838383041417156151ae57905090500461080051600281116151ae5760051b6106e00152600101818118613d80575050602061062051600281116151ae5760051b6152310161080039600b546108205242610820511115613f7b5761078051610800518082028115838383041417156151ae5790509050610780526106205115613ec357670de0b6b3a76400006107805161062051600181038181116151ae579050600181116151ae5760051b6107c001518082028115838383041417156151ae579050905004610780525b61062051600281116151ae5760051b6106e00151610840526107805161062051600281116151ae5760051b6106e001526020615291600039600051637b12e009610860526106a051610880526106c0516108a0526106e0516108c052610700516108e0526107205161090052600061092052602061086060c461087c845afa613f51573d600060003e3d6000fd5b60203d106151ae57610860905051600f556108405161062051600281116151ae5760051b6106e001525b600f54610840526020615291600039600051634a2ab3be6108a0526106a0516108c0526106c0516108e0526106e05161090052610700516109205261072051610940526108405161096052610640516109805260406108a060e46108bc845afa613fea573d600060003e3d6000fd5b60403d106151ae576108a090508051610860526020810151610880525061064051600281116151ae5760051b6106e00151610860518082038281116151ae57905090506107405261064051600281116151ae5760051b6106e0018051610740518082038281116151ae579050905081525061074051600181038181116151ae5790506107405261064051156140ce5761074051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae57905061064051600181038181116151ae579050600181116151ae5760051b6107c0015180156151ae5780820490509050610740525b61074051602061064051600281116151ae5760051b6152310160003960005180156151ae5780820490509050610740526402540be4006106e051606052610700516080526107205160a0526141246108c0612db1565b6108c051610740518082028115838383041417156151ae5790509050046108a052610740516108a0518082038281116151ae579050905061074052610680516107405110156141d35760086108c0527f536c6970706167650000000000000000000000000000000000000000000000006108e0526108c0506108c051806108e001601f826000031636823750506308c379a06108805260206108a052601f19601f6108c051011660440161089cfd5b61076051610740518082038281116151ae57905090506107605261076051602061064051600281116151ae5760051b615231016000396000518082028115838383041417156151ae579050905061076052610640511561427557670de0b6b3a76400006107605161064051600181038181116151ae579050600181116151ae5760051b6107c001518082028115838383041417156151ae579050905004610760525b6107605161064051600281116151ae5760051b6106e001526106a05160e0526106c051610100526106e0516101205261070051610140526107205161016052600061018052610880516101a0526142cd6108c0612fea565b6108c0516107a0526107405181526108a05160208201526107a051604082015250565b60603660803760405160038104905060805260605160e052600160028101905b8061010052604051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506fffffffffffffffffffffffffffffffff60e05116600381028160038204186151ae57905080156151ae578082049050905061010051600281116151ae5760051b6080015260e05160801c60e052600101818118614310575050602061529160003960005163bad1dc26610100526080516101205260a0516101405260c051610160526020610100606461011c845afa6143d5573d600060003e3d6000fd5b60203d106151ae57610100905051815250565b600361024051606052610260516080526102805160a05261440a6102c0612db1565b6102c0510260031c6102a05260006102c05260006003905b8060051b6101e001516102e0526102c0516102e0518082018281106151ae57905090506102c05260010181811861442257505060036102c051046102e05260006103005260006003905b8060051b6101e00151610320526102e05161032051116144aa5761030051610320516102e051038082018281106151ae5790509050610300526144ca565b610300516102e05161032051038082018281106151ae5790509050610300525b60010181811861446c5750506102a051610300518082028115838383041417156151ae57905090506102c05180156151ae5780820490509050620186a081018181106151ae579050815250565b601a546060518082018281106151ae5790509050601a556018604051602052600052604060002080546060518082018281106151ae579050905081555060405160007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601a546060518082038281116151ae5790509050601a556018604051602052600052604060002080546060518082038281116151ae579050905081555060006040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b60405160c05260605160e0526080516101005260c05160206152316000396000518082028115838383041417156151ae579050905060c05260a05161012052600160028101905b80610140526fffffffffffffffffffffffffffffffff6101205116602061014051600281116151ae5760051b615231016000396000518082028115838383041417156151ae57905090506101605261014051600281116151ae5760051b60c00151610160518082028115838383041417156151ae5790509050670de0b6b3a76400008104905061014051600281116151ae5760051b60c001526101205160801c6101205260010181811861464857505060c051815260e051602082015261010051604082015250565b601654610180526201517f61018051420311156147325742600b5411614735565b60015b1561473f57614b05565b6010546101a0526011546101c052601a546101e0526101c0516101a051111561477657670de0b6b3a763ffff6101e0511115614779565b60015b1561478357614b05565b61478e610240612bf6565b61024080516102005260208101516102205250600f54610240526012546102605260015461028052602061531160003960005163cab4d3db6102c05260206102c060046102dc845afa6147e6573d600060003e3d6000fd5b60203d106151ae576102c0518060a01c6151ae57610300526103009050516102a052600c546102c052600d546102e052600e54610300526404a817c8006101c0516101a051036015548082028115838383041417156151ae57905090500461032052601754610340526000610360526102a051156148695761032051151561486c565b60005b156149385761026051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae57905061026051610320518082038281116151ae579050905080156151ae5780820490509050670de0b6b3a764000081038181116151ae57905061036052610340516101e051610360518082028115838383041417156151ae5790509050670de0b6b3a7640000810490508082018281106151ae5790509050610340526101a051610320518060011b818160011c186151ae5790508082038281116151ae57905090506101a0525b6101e051610340518082018281106151ae579050905061038052610240516040526102805160605261496b6103a06142f0565b6103a051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506103805180156151ae578082049050905061026052670de0b6b3a763ffff61026051116149b957614b05565b60006017556101a0516010554260165561026051601255610240516103805161024051610340518082028115838383041417156151ae5790509050048082038281116151ae5790509050600f556101c0516101a0511115614a1c576101a0516011555b6060366103a0376103405115614b055760006003905b806104005261040051600281116151ae5760051b6102c00151610340518082028115838383041417156151ae57905090506103805180156151ae578082049050905061040051600281116151ae5760051b6103a001526104005160405261040051600281116151ae5760051b6103a001516060526102a051608052614ab5612b49565b600101818118614a325750506102a0517f5ebe66c628d0969a211b99673a322baa5fc8a62baf286c175e254daef76f5bcc6103a051610400526103c051610420526103e051610440526060610400a25b565b601a54610280526102805161022051116151ae57600261024051116151ae57600c546102a052600d546102c052600e546102e0526060615231610300396000610360526020615231600039600051670de0b6b3a7640000810281670de0b6b3a76400008204186151ae579050610380526001546103a052610300516102a0518082028115838383041417156151ae579050905061030052600160028101905b806103c0526fffffffffffffffffffffffffffffffff6103a051166103e0526103c0516102405118614c03576103e05161024051600281116151ae5760051b61030001518082028115838383041417156151ae5790509050610380525b670de0b6b3a76400006103c051600281116151ae5760051b61030001516103c051600281116151ae5760051b6102a001518082028115838383041417156151ae57905090506103e0518082028115838383041417156151ae5790509050046103c051600281116151ae5760051b61030001526103a05160801c6103a052600101818118614ba657505061026051614ca057600f5461036052614d11565b6020615291600039600051637b12e0096103c0526101e0516103e052610200516104005261030051610420526103205161044052610340516104605260006104805260206103c060c46103dc845afa614cfe573d600060003e3d6000fd5b60203d106151ae576103c0905051610360525b610360516103c052610300516103e0526103205161040052610340516104205261024051600281116151ae5760051b6103000151600381028160038204186151ae579050610220518082028115838383041417156151ae57905090506102805180156151ae578082049050905061044052601454604052614d93610480612921565b610480602081019050516104605261024051600281116151ae5760051b6103e00151610440511015614e125761024051600281116151ae5760051b6103e0018051610440518082038281116151ae57905090508152506103e051606052610400516080526104205160a052614e09610480612db1565b61048051610460525b61028051610220516103c0518082028115838383041417156151ae5790509050046104805261046051610480518082028115838383041417156151ae57905090506404a817c80081049050600181018181106151ae5790506104a0526104a051600381028160038204186151ae57905061024051600281116151ae5760051b6102a001518082028115838383041417156151ae57905090506103c05180156151ae57808204905090506104c0526103c051610480516104a0518082038281116151ae57905090508082038281116151ae57905090506103c0526020615291600039600051634a2ab3be610500526101e05161052052610200516105405261030051610560526103205161058052610340516105a0526103c0516105c052610240516105e052604061050060e461051c845afa614f53573d600060003e3d6000fd5b60403d106151ae576105009050516104e05261024051600281116151ae5760051b61030001516104e0518082038281116151ae5790509050670de0b6b3a7640000810281670de0b6b3a76400008204186151ae5790506103805180156151ae5780820490509050610500526104e05161024051600281116151ae5760051b61030001526105005181526103c051602082015260408101610300518152610320516020820152610340516040820152506104c05160a082015250565b60805160605160401b60405160801b1717815250565b608051601960405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b60605130811461508f57801515615092565b60005b9050156151ae576018604051602052600052604060002080546080518082038281116151ae57905090508155506018606051602052600052604060002080546080518082018281106151ae57905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60206153f160003960005146146151a3577fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647260605260206153d16080397f5dbdb28f8f4d108c1e350752a188c57fb3bbc96e9092edc7a160ae3f13c8c51660a0524660c0523060e05260206154116101003960c060405260408051602082012090508152506151ac565b60206154318239505b565b600080fd031623f21b9425552380291b0fec177f25d7147902f6291b1049252f291b291b078d2607291b119d0f6f24ad226a02160115291b0358019a115c1ef105300f8f291b2876003c07c9262925ad291b291b001a291b066a21c6133e11bd291b10d9291b017a2108291b0271149c1f3b2433291b291b291b2167291b291b291b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cbff3004a20dbfe2731543aa38599a526e0fd6ee000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000ad038eb671c44b853887a7e32528fab35dc5d71000000000000000000000000041d5d79431a913c4ae7d69a668ecdfe5ff9dfb680000000000000000000000000c0e5f2ff0ff18a3be9b835635039256dc4b49630000000000000000000000000000000000000000000000000000000000000006547269444252000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009637276444252494e560000000000000000000000000000000000000000000000e0fbf3a738434f75293c48820e14ab11f22bf0c43a6dfdc979e3ce3b050c74960000000000000000000000000000000000000000000000000000000000000001b11deb43b9dd04fdd7965e6bba8b4e96b4472e311c56324004cceddd8df31b078322661f6059d6e542ecb482618894eacd889a4f6f80c3b1f34846a821d955c3
Verified Source Code Partial Match
Compiler: v0.3.10+commit.91361694
CurveTricryptoOptimized.vy 2099 lines
# pragma version 0.3.10
# pragma optimize gas
# pragma evm-version paris
"""
@title CurveTricryptoOptimized
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2023 - all rights reserved
@notice A Curve AMM pool for 3 unpegged assets (e.g. WETH, BTC, USD).
@dev All prices in the AMM are with respect to the first token in the pool.
"""
from vyper.interfaces import ERC20
implements: ERC20 # <--------------------- AMM contract is also the LP token.
# --------------------------------- Interfaces -------------------------------
interface Math:
def geometric_mean(_x: uint256[N_COINS]) -> uint256: view
def wad_exp(_power: int256) -> uint256: view
def cbrt(x: uint256) -> uint256: view
def reduction_coefficient(
x: uint256[N_COINS], fee_gamma: uint256
) -> uint256: view
def newton_D(
ANN: uint256,
gamma: uint256,
x_unsorted: uint256[N_COINS],
K0_prev: uint256
) -> uint256: view
def get_y(
ANN: uint256,
gamma: uint256,
x: uint256[N_COINS],
D: uint256,
i: uint256,
) -> uint256[2]: view
def get_p(
_xp: uint256[N_COINS], _D: uint256, _A_gamma: uint256[2],
) -> uint256[N_COINS-1]: view
interface Factory:
def admin() -> address: view
def fee_receiver() -> address: view
def views_implementation() -> address: view
interface Views:
def calc_token_amount(
amounts: uint256[N_COINS], deposit: bool, swap: address
) -> uint256: view
def get_dy(
i: uint256, j: uint256, dx: uint256, swap: address
) -> uint256: view
def get_dx(
i: uint256, j: uint256, dy: uint256, swap: 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: uint256
tokens_sold: uint256
bought_id: uint256
tokens_bought: uint256
fee: uint256
packed_price_scale: uint256
event AddLiquidity:
provider: indexed(address)
token_amounts: uint256[N_COINS]
fee: uint256
token_supply: uint256
packed_price_scale: uint256
event RemoveLiquidity:
provider: indexed(address)
token_amounts: uint256[N_COINS]
token_supply: uint256
event RemoveLiquidityOne:
provider: indexed(address)
token_amount: uint256
coin_index: uint256
coin_amount: uint256
approx_fee: uint256
packed_price_scale: uint256
event NewParameters:
mid_fee: uint256
out_fee: uint256
fee_gamma: uint256
allowed_extra_profit: uint256
adjustment_step: uint256
ma_time: uint256
xcp_ma_time: uint256
event RampAgamma:
initial_A: uint256
future_A: uint256
initial_gamma: uint256
future_gamma: uint256
initial_time: uint256
future_time: uint256
event StopRampA:
current_A: uint256
current_gamma: uint256
time: uint256
event ClaimAdminFee:
admin: indexed(address)
tokens: uint256[N_COINS]
event SetAdminFee:
admin_fee: uint256
# ----------------------- Storage/State Variables ----------------------------
N_COINS: constant(uint256) = 3
PRECISION: constant(uint256) = 10**18 # <------- The precision to convert to.
PRECISIONS: immutable(uint256[N_COINS])
MATH: public(immutable(Math))
coins: public(immutable(address[N_COINS]))
factory: public(immutable(Factory))
price_scale_packed: uint256 # <------------------------ Internal price scale.
price_oracle_packed: uint256 # <------- Price target given by moving average.
cached_xcp_oracle: uint256 # <----------- EMA of totalSupply * virtual_price.
last_prices_packed: uint256
last_timestamp: public(uint256) # idx 0 is for prices, idx 1 is for xcp.
last_xcp: public(uint256)
xcp_ma_time: public(uint256)
initial_A_gamma: public(uint256)
initial_A_gamma_time: public(uint256)
future_A_gamma: public(uint256)
future_A_gamma_time: public(uint256) # <------ Time when ramping is finished.
# This value is 0 (default) when pool is first deployed, and only gets
# populated by block.timestamp + future_time in `ramp_A_gamma` when the
# ramping process is initiated. After ramping is finished
# (i.e. self.future_A_gamma_time < block.timestamp), the variable is left
# and not set to 0.
balances: public(uint256[N_COINS])
D: public(uint256)
xcp_profit: public(uint256)
xcp_profit_a: public(uint256) # <--- Full profit at last claim of admin fees.
virtual_price: public(uint256) # <------ Cached (fast to read) virtual price.
# The cached `virtual_price` is also used internally.
# Params that affect how price_scale get adjusted :
packed_rebalancing_params: public(uint256) # <---------- Contains rebalancing
# parameters allowed_extra_profit, adjustment_step, and ma_time.
# Fee params that determine dynamic fees:
packed_fee_params: public(uint256) # <---- Packs mid_fee, out_fee, fee_gamma.
MIN_FEE: constant(uint256) = 5 * 10**5 # <-------------------------- 0.5 BPS.
MAX_FEE: constant(uint256) = 10 * 10**9
NOISE_FEE: constant(uint256) = 10**5 # <---------------------------- 0.1 BPS.
# ----------------------- Admin params ---------------------------------------
admin_fee: public(uint256)
last_admin_fee_claim_timestamp: uint256
admin_lp_virtual_balance: uint256
MIN_RAMP_TIME: constant(uint256) = 86400
MIN_ADMIN_FEE_CLAIM_INTERVAL: constant(uint256) = 86400
A_MULTIPLIER: constant(uint256) = 10000
MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 100
MAX_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER * 1000
MAX_A_CHANGE: constant(uint256) = 10
MIN_GAMMA: constant(uint256) = 10**10
MAX_GAMMA: constant(uint256) = 5 * 10**16
PRICE_SIZE: constant(uint128) = 256 / (N_COINS - 1)
PRICE_MASK: constant(uint256) = 2**PRICE_SIZE - 1
# ----------------------- ERC20 Specific vars --------------------------------
name: public(immutable(String[64]))
symbol: public(immutable(String[32]))
decimals: public(constant(uint8)) = 18
version: public(constant(String[8])) = "v2.0.1"
balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
totalSupply: public(uint256)
nonces: public(HashMap[address, uint256])
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)
# ----------------------- Contract -------------------------------------------
@external
def __init__(
_name: String[64],
_symbol: String[32],
_coins: address[N_COINS],
_math: address,
_weth: address, # unused but factory has it.
_salt: bytes32,
__packed_precisions: uint256,
packed_A_gamma: uint256,
packed_fee_params: uint256,
packed_rebalancing_params: uint256,
packed_prices: uint256,
):
MATH = Math(_math)
factory = Factory(msg.sender)
name = _name
symbol = _symbol
coins = _coins
PRECISIONS = self._unpack_3(__packed_precisions) # <------- Precisions of
# coins are calculated as 10**(18 - coin.decimals()).
self.initial_A_gamma = packed_A_gamma # <------------------- A and gamma.
self.future_A_gamma = packed_A_gamma
self.packed_rebalancing_params = packed_rebalancing_params # <-- Contains
# rebalancing params: allowed_extra_profit, adjustment_step,
# and ma_exp_time.
self.packed_fee_params = packed_fee_params # <-------------- Contains Fee
# params: mid_fee, out_fee and fee_gamma.
self.price_scale_packed = packed_prices
self.price_oracle_packed = packed_prices
self.last_prices_packed = packed_prices
self.last_timestamp = self._pack_2(block.timestamp, block.timestamp)
self.xcp_profit_a = 10**18
self.xcp_ma_time = 62324 # <--------- 12 hours default on contract start.
# Cache DOMAIN_SEPARATOR. If chain.id is not CACHED_CHAIN_ID, then
# DOMAIN_SEPARATOR will be re-calculated each time `permit` is called.
# Otherwise, it will always use CACHED_DOMAIN_SEPARATOR.
# see: `_domain_separator()` for its implementation.
NAME_HASH = keccak256(name)
salt = _salt
CACHED_CHAIN_ID = chain.id
CACHED_DOMAIN_SEPARATOR = keccak256(
_abi_encode(
EIP712_TYPEHASH,
NAME_HASH,
VERSION_HASH,
chain.id,
self,
salt,
)
)
self.admin_fee = 5 * 10**9 # 50%
log Transfer(empty(address), self, 0) # <------- Fire empty transfer from
# 0x0 to self for indexers to catch.
# ------------------- Token transfers in and out of the AMM ------------------
@internal
def _transfer_in(
_coin_idx: uint256,
_dx: uint256,
sender: address,
expect_optimistic_transfer: bool,
) -> uint256:
"""
@notice Transfers `_coin` from `sender` to `self` and calls `callback_sig`
if it is not empty.
@params _coin_idx uint256 Index of the coin to transfer in.
@params dx amount of `_coin` to transfer into the pool.
@params sender address to transfer `_coin` from.
@params expect_optimistic_transfer bool True if pool expects user to transfer.
This is only enabled for exchange_received.
@return The amount of tokens received.
"""
coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self)
if expect_optimistic_transfer: # Only enabled in exchange_received:
# it expects the caller of exchange_received to have sent tokens to
# the pool before calling this method.
# If someone donates extra tokens to the contract: do not acknowledge.
# We only want to know if there are dx amount of tokens. Anything extra,
# we ignore. This is why we need to check if received_amounts (which
# accounts for coin balances of the contract) is atleast dx.
# If we checked for received_amounts == dx, an extra transfer without a
# call to exchange_received will break the method.
dx: uint256 = coin_balance - self.balances[_coin_idx]
assert dx >= _dx # dev: user didn't give us coins
# Adjust balances
self.balances[_coin_idx] += dx
return dx
# ----------------------------------------------- ERC20 transferFrom flow.
# EXTERNAL CALL
assert ERC20(coins[_coin_idx]).transferFrom(
sender,
self,
_dx,
default_return_value=True
)
dx: uint256 = ERC20(coins[_coin_idx]).balanceOf(self) - coin_balance
self.balances[_coin_idx] += dx
return dx
@internal
def _transfer_out(_coin_idx: uint256, _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`, `_claim_admin_fees` and `_exchange` methods.
@params _coin_idx uint256 Index of the token to transfer out
@params _amount Amount of token to transfer out
@params receiver Address to send the tokens to
"""
# Adjust balances before handling transfers:
self.balances[_coin_idx] -= _amount
# EXTERNAL CALL
assert ERC20(coins[_coin_idx]).transfer(
receiver,
_amount,
default_return_value=True
)
# -------------------------- AMM Main Functions ------------------------------
@external
@nonreentrant("lock")
def exchange(
i: uint256,
j: uint256,
dx: uint256,
min_dy: uint256,
receiver: address = msg.sender
) -> uint256:
"""
@notice Exchange using wrapped native token by default
@param i Index value for the input coin
@param j Index value for the output coin
@param dx Amount of input coin being swapped in
@param min_dy Minimum amount of output coin to receive
@param receiver Address to send the output coin to. Default is msg.sender
@return uint256 Amount of tokens at index j received by the `receiver
"""
# _transfer_in updates self.balances here:
dx_received: uint256 = self._transfer_in(
i,
dx,
msg.sender,
False
)
# No ERC20 token transfers occur here:
out: uint256[3] = self._exchange(
i,
j,
dx_received,
min_dy,
)
# _transfer_out updates self.balances here. Update to state occurs before
# external calls:
self._transfer_out(j, out[0], receiver)
# log:
log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2])
return out[0]
@external
@nonreentrant('lock')
def exchange_received(
i: uint256,
j: uint256,
dx: uint256,
min_dy: uint256,
receiver: address = msg.sender,
) -> uint256:
"""
@notice Exchange: but user must transfer dx amount of coin[i] tokens to pool first.
Pool will not call transferFrom and will only check if a surplus of
coins[i] is greater than or equal to `dx`.
@dev Use-case is to reduce the number of redundant ERC20 token
transfers in zaps. Primarily for dex-aggregators/arbitrageurs/searchers.
Note for users: please transfer + exchange_received in 1 tx.
@param i Index value for the input coin
@param j Index value for the output coin
@param dx Amount of input coin being swapped in
@param min_dy Minimum amount of output coin to receive
@param receiver Address to send the output coin to
@return uint256 Amount of tokens at index j received by the `receiver`
"""
# _transfer_in updates self.balances here:
dx_received: uint256 = self._transfer_in(
i,
dx,
msg.sender,
True # <---- expect_optimistic_transfer is set to True here.
)
# No ERC20 token transfers occur here:
out: uint256[3] = self._exchange(
i,
j,
dx_received,
min_dy,
)
# _transfer_out updates self.balances here. Update to state occurs before
# external calls:
self._transfer_out(j, out[0], receiver)
# log:
log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2])
return out[0]
@external
@nonreentrant("lock")
def add_liquidity(
amounts: uint256[N_COINS],
min_mint_amount: uint256,
receiver: address = msg.sender
) -> uint256:
"""
@notice Adds liquidity into the pool.
@param amounts Amounts of each coin to add.
@param min_mint_amount Minimum amount of LP to mint.
@param receiver Address to send the LP tokens to. Default is msg.sender
@return uint256 Amount of LP tokens received by the `receiver
"""
A_gamma: uint256[2] = self._A_gamma()
xp: uint256[N_COINS] = self.balances
amountsp: uint256[N_COINS] = empty(uint256[N_COINS])
d_token: uint256 = 0
d_token_fee: uint256 = 0
old_D: uint256 = 0
assert amounts[0] + amounts[1] + amounts[2] > 0 # dev: no coins to add
# --------------------- Get prices, balances -----------------------------
packed_price_scale: uint256 = self.price_scale_packed
price_scale: uint256[N_COINS-1] = self._unpack_prices(packed_price_scale)
# -------------------------------------- Update balances and calculate xp.
xp_old: uint256[N_COINS] = xp
amounts_received: uint256[N_COINS] = empty(uint256[N_COINS])
########################## TRANSFER IN <-------
for i in range(N_COINS):
if amounts[i] > 0:
# Updates self.balances here:
amounts_received[i] = self._transfer_in(
i,
amounts[i],
msg.sender,
False, # <--------------------- Disable optimistic transfers.
)
xp[i] = xp[i] + amounts_received[i]
xp[0] *= PRECISIONS[0]
xp_old[0] *= PRECISIONS[0]
for i in range(N_COINS):
if i >= 1:
xp[i] = unsafe_div(xp[i] * price_scale[i-1] * PRECISIONS[i], PRECISION)
xp_old[i] = unsafe_div(
xp_old[i] * unsafe_mul(price_scale[i-1], PRECISIONS[i]),
PRECISION
)
if amounts_received[i] > 0:
amountsp[i] = xp[i] - xp_old[i]
# -------------------- Calculate LP tokens to mint -----------------------
if self.future_A_gamma_time > block.timestamp: # <--- A_gamma is ramping.
# ----- Recalculate the invariant if A or gamma are undergoing a ramp.
old_D = MATH.newton_D(A_gamma[0], A_gamma[1], xp_old, 0)
else:
old_D = self.D
D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
token_supply: uint256 = self.totalSupply
if old_D > 0:
d_token = token_supply * D / old_D - token_supply
else:
d_token = self.get_xcp(D, packed_price_scale) # <----- Making initial
# virtual price equal to 1.
assert d_token > 0 # dev: nothing minted
if old_D > 0:
d_token_fee = (
self._calc_token_fee(amountsp, xp) * d_token / 10**10 + 1
)
d_token -= d_token_fee
token_supply += d_token
self.mint(receiver, d_token)
self.admin_lp_virtual_balance += unsafe_div(self.admin_fee * d_token_fee, 10**10)
packed_price_scale = self.tweak_price(A_gamma, xp, D, 0)
else:
# (re)instatiating an empty pool:
self.D = D
self.virtual_price = 10**18
self.xcp_profit = 10**18
self.xcp_profit_a = 10**18
# Initialise xcp oracle here:
self.cached_xcp_oracle = d_token # <--- virtual_price * totalSupply / 10**18
self.mint(receiver, d_token)
assert d_token >= min_mint_amount, "Slippage"
# ---------------------------------------------- Log and claim admin fees.
log AddLiquidity(
receiver, amounts_received, d_token_fee, token_supply, packed_price_scale
)
return d_token
@external
@nonreentrant("lock")
def remove_liquidity(
_amount: uint256,
min_amounts: uint256[N_COINS],
receiver: address = msg.sender,
) -> uint256[N_COINS]:
"""
@notice This withdrawal method is very safe, does no complex math since
tokens are withdrawn in balanced proportions. No fees are charged.
@param _amount Amount of LP tokens to burn
@param min_amounts Minimum amounts of tokens to withdraw
@param receiver Address to send the withdrawn tokens to
@return uint256[3] Amount of pool tokens received by the `receiver`
"""
amount: uint256 = _amount
balances: uint256[N_COINS] = self.balances
withdraw_amounts: uint256[N_COINS] = empty(uint256[N_COINS])
# -------------------------------------------------------- Burn LP tokens.
total_supply: uint256 = self.totalSupply # <------ Get totalSupply before
self.burnFrom(msg.sender, _amount) # ---- reducing it with self.burnFrom.
# There are two cases for withdrawing tokens from the pool.
# Case 1. Withdrawal does not empty the pool.
# In this situation, D is adjusted proportional to the amount of
# LP tokens burnt. ERC20 tokens transferred is proportional
# to : (AMM balance * LP tokens in) / LP token total supply
# Case 2. Withdrawal empties the pool.
# In this situation, all tokens are withdrawn and the invariant
# is reset.
if amount == total_supply: # <----------------------------------- Case 2.
for i in range(N_COINS):
withdraw_amounts[i] = balances[i]
else: # <-------------------------------------------------------- Case 1.
amount -= 1 # <---- To prevent rounding errors, favor LPs a tiny bit.
for i in range(N_COINS):
withdraw_amounts[i] = balances[i] * amount / total_supply
assert withdraw_amounts[i] >= min_amounts[i]
D: uint256 = self.D
self.D = D - unsafe_div(D * amount, total_supply) # <----------- Reduce D
# proportional to the amount of tokens leaving. Since withdrawals are
# balanced, this is a simple subtraction. If amount == total_supply,
# D will be 0.
# ---------------------------------- Transfers ---------------------------
for i in range(N_COINS):
# _transfer_out updates self.balances here. Update to state occurs
# before external calls:
self._transfer_out(i, withdraw_amounts[i], receiver)
log RemoveLiquidity(msg.sender, withdraw_amounts, total_supply - _amount)
# --------------------------- Upkeep xcp oracle --------------------------
# Update xcp since liquidity was removed:
xp: uint256[N_COINS] = self.xp(self.balances, self.price_scale_packed)
last_xcp: uint256 = MATH.geometric_mean(xp) # <----------- Cache it for now.
last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp)
if last_timestamp[1] < block.timestamp:
cached_xcp_oracle: uint256 = self.cached_xcp_oracle
alpha: uint256 = self._alpha(last_timestamp[1], self.xcp_ma_time)
self.cached_xcp_oracle = unsafe_div(
last_xcp * (10**18 - alpha) + cached_xcp_oracle * alpha,
10**18
)
last_timestamp[1] = block.timestamp
# Pack and store timestamps:
self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1])
# Store last xcp
self.last_xcp = last_xcp
return withdraw_amounts
@external
@nonreentrant("lock")
def remove_liquidity_one_coin(
token_amount: uint256,
i: uint256,
min_amount: uint256,
receiver: address = msg.sender
) -> uint256:
"""
@notice Withdraw liquidity in a single token.
Involves fees (lower than swap fees).
@dev This operation also involves an admin fee claim.
@param token_amount Amount of LP tokens to burn
@param i Index of the token to withdraw
@param min_amount Minimum amount of token to withdraw.
@param receiver Address to send the withdrawn tokens to
@return Amount of tokens at index i received by the `receiver`
"""
self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally.
A_gamma: uint256[2] = self._A_gamma()
dy: uint256 = 0
D: uint256 = 0
p: uint256 = 0
xp: uint256[N_COINS] = empty(uint256[N_COINS])
approx_fee: uint256 = 0
# ------------------------------------------------------------------------
dy, D, xp, approx_fee = self._calc_withdraw_one_coin(
A_gamma,
token_amount,
i,
(self.future_A_gamma_time > block.timestamp), # <------- During ramps
) # we need to update D.
assert dy >= min_amount, "Slippage"
# ---------------------------- State Updates -----------------------------
# Burn user's tokens:
self.burnFrom(msg.sender, token_amount)
packed_price_scale: uint256 = self.tweak_price(A_gamma, xp, D, 0)
# Safe to use D from _calc_withdraw_one_coin here ---^
# ------------------------- Transfers ------------------------------------
# _transfer_out updates self.balances here. Update to state occurs before
# external calls:
self._transfer_out(i, dy, receiver)
log RemoveLiquidityOne(
msg.sender, token_amount, i, dy, approx_fee, packed_price_scale
)
return dy
# -------------------------- Packing functions -------------------------------
@internal
@pure
def _pack_3(x: uint256[3]) -> uint256:
"""
@notice Packs 3 integers with values <= 10**18 into a uint256
@param x The uint256[3] to pack
@return uint256 Integer with packed values
"""
return (x[0] << 128) | (x[1] << 64) | x[2]
@internal
@pure
def _unpack_3(_packed: uint256) -> uint256[3]:
"""
@notice Unpacks a uint256 into 3 integers (values must be <= 10**18)
@param val The uint256 to unpack
@return uint256[3] A list of length 3 with unpacked integers
"""
return [
(_packed >> 128) & 18446744073709551615,
(_packed >> 64) & 18446744073709551615,
_packed & 18446744073709551615,
]
@pure
@internal
def _pack_2(p1: uint256, p2: uint256) -> uint256:
return p1 | (p2 << 128)
@pure
@internal
def _unpack_2(packed: uint256) -> uint256[2]:
return [packed & (2**128 - 1), packed >> 128]
@internal
@pure
def _pack_prices(prices_to_pack: uint256[N_COINS-1]) -> uint256:
"""
@notice Packs N_COINS-1 prices into a uint256.
@param prices_to_pack The prices to pack
@return uint256 An integer that packs prices
"""
packed_prices: uint256 = 0
p: uint256 = 0
for k in range(N_COINS - 1):
packed_prices = packed_prices << PRICE_SIZE
p = prices_to_pack[N_COINS - 2 - k]
assert p < PRICE_MASK
packed_prices = p | packed_prices
return packed_prices
@internal
@pure
def _unpack_prices(_packed_prices: uint256) -> uint256[2]:
"""
@notice Unpacks N_COINS-1 prices from a uint256.
@param _packed_prices The packed prices
@return uint256[2] Unpacked prices
"""
unpacked_prices: uint256[N_COINS-1] = empty(uint256[N_COINS-1])
packed_prices: uint256 = _packed_prices
for k in range(N_COINS - 1):
unpacked_prices[k] = packed_prices & PRICE_MASK
packed_prices = packed_prices >> PRICE_SIZE
return unpacked_prices
# ---------------------- AMM Internal Functions -------------------------------
@internal
def _exchange(
i: uint256,
j: uint256,
dx_received: uint256,
min_dy: uint256,
) -> uint256[3]:
assert i != j # dev: coin index out of range
assert dx_received > 0 # dev: do not exchange 0 coins
A_gamma: uint256[2] = self._A_gamma()
xp: uint256[N_COINS] = self.balances # <------- Has dx added to balances.
dy: uint256 = 0
y: uint256 = xp[j] # <----------------- if j > N_COINS, this will revert.
x0: uint256 = xp[i] - dx_received # old xp[i]
packed_price_scale: uint256 = self.price_scale_packed
price_scale: uint256[N_COINS - 1] = self._unpack_prices(
packed_price_scale
)
xp[0] *= PRECISIONS[0]
for k in range(1, N_COINS):
xp[k] = unsafe_div(
xp[k] * price_scale[k - 1] * PRECISIONS[k],
PRECISION
) # <-------- Safu to do unsafe_div here since PRECISION is not zero.
prec_i: uint256 = PRECISIONS[i]
# ----------- Update invariant if A, gamma are undergoing ramps ---------
t: uint256 = self.future_A_gamma_time
if t > block.timestamp:
x0 *= prec_i
if i > 0:
x0 = unsafe_div(x0 * price_scale[i - 1], PRECISION)
x1: uint256 = xp[i] # <------------------ Back up old value in xp ...
xp[i] = x0 # |
self.D = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) # |
xp[i] = x1 # <-------------------------------------- ... and restore.
# ----------------------- Calculate dy and fees --------------------------
D: uint256 = self.D
y_out: uint256[2] = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, j)
dy = xp[j] - y_out[0]
xp[j] -= dy
dy -= 1
if j > 0:
dy = dy * PRECISION / price_scale[j - 1]
dy /= PRECISIONS[j]
fee: uint256 = unsafe_div(self._fee(xp) * dy, 10**10)
dy -= fee # <--------------------- Subtract fee from the outgoing amount.
assert dy >= min_dy, "Slippage"
y -= dy
y *= PRECISIONS[j]
if j > 0:
y = unsafe_div(y * price_scale[j - 1], PRECISION)
xp[j] = y # <------------------------------------------------- Update xp.
# ------ Tweak price_scale with good initial guess for newton_D ----------
packed_price_scale = self.tweak_price(A_gamma, xp, 0, y_out[1])
return [dy, fee, packed_price_scale]
@internal
def tweak_price(
A_gamma: uint256[2],
_xp: uint256[N_COINS],
new_D: uint256,
K0_prev: uint256 = 0,
) -> uint256:
"""
@notice Updates price_oracle, last_price and conditionally adjusts
price_scale. This is called whenever there is an unbalanced
liquidity operation: _exchange, add_liquidity, or
remove_liquidity_one_coin.
@dev Contains main liquidity rebalancing logic, by tweaking `price_scale`.
@param A_gamma Array of A and gamma parameters.
@param _xp Array of current balances.
@param new_D New D value.
@param K0_prev Initial guess for `newton_D`.
"""
# ---------------------------- Read storage ------------------------------
price_oracle: uint256[N_COINS - 1] = self._unpack_prices(self.price_oracle_packed)
last_prices: uint256[N_COINS - 1] = self._unpack_prices(self.last_prices_packed)
packed_price_scale: uint256 = self.price_scale_packed
price_scale: uint256[N_COINS - 1] = self._unpack_prices(packed_price_scale)
rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)
# Contains: allowed_extra_profit, adjustment_step, ma_time. -----^
total_supply: uint256 = self.totalSupply
old_xcp_profit: uint256 = self.xcp_profit
old_virtual_price: uint256 = self.virtual_price
# ----------------------- Update Oracles if needed -----------------------
last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp)
alpha: uint256 = 0
if last_timestamp[0] < block.timestamp: # 0th index is for price_oracle.
# The moving average price oracle is calculated using the last_price
# of the trade at the previous block, and the price oracle logged
# before that trade. This can happen only once per block.
# ------------------ Calculate moving average params -----------------
alpha = self._alpha(last_timestamp[0], rebalancing_params[2])
for k in range(N_COINS - 1):
# ----------------- We cap state price that goes into the EMA with
# 2 x price_scale.
price_oracle[k] = unsafe_div(
min(last_prices[k], 2 * price_scale[k]) * (10**18 - alpha) +
price_oracle[k] * alpha, # ^-------- Cap spot price into EMA.
10**18
)
self.price_oracle_packed = self._pack_prices(price_oracle)
last_timestamp[0] = block.timestamp
# ----------------------------------------------------- Update xcp oracle.
if last_timestamp[1] < block.timestamp:
cached_xcp_oracle: uint256 = self.cached_xcp_oracle
alpha = self._alpha(last_timestamp[1], self.xcp_ma_time)
self.cached_xcp_oracle = unsafe_div(
self.last_xcp * (10**18 - alpha) + cached_xcp_oracle * alpha,
10**18
)
# Pack and store timestamps:
last_timestamp[1] = block.timestamp
self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1])
# `price_oracle` is used further on to calculate its vector distance from
# price_scale. This distance is used to calculate the amount of adjustment
# to be done to the price_scale.
# ------------------------------------------------------------------------
# ------------------ If new_D is set to 0, calculate it ------------------
D_unadjusted: uint256 = new_D
if new_D == 0: # <--------------------------- _exchange sets new_D to 0.
D_unadjusted = MATH.newton_D(A_gamma[0], A_gamma[1], _xp, K0_prev)
# ----------------------- Calculate last_prices --------------------------
last_prices = MATH.get_p(_xp, D_unadjusted, A_gamma)
for k in range(N_COINS - 1):
last_prices[k] = unsafe_div(last_prices[k] * price_scale[k], 10**18)
self.last_prices_packed = self._pack_prices(last_prices)
# ---------- Update profit numbers without price adjustment first --------
xp: uint256[N_COINS] = empty(uint256[N_COINS])
xp[0] = unsafe_div(D_unadjusted, N_COINS)
for k in range(N_COINS - 1):
xp[k + 1] = D_unadjusted * 10**18 / (N_COINS * price_scale[k])
# ------------------------- Update xcp_profit ----------------------------
xcp_profit: uint256 = 10**18
virtual_price: uint256 = 10**18
if old_virtual_price > 0:
xcp: uint256 = MATH.geometric_mean(xp)
virtual_price = 10**18 * xcp / total_supply
xcp_profit = unsafe_div(
old_xcp_profit * virtual_price,
old_virtual_price
) # <---------------- Safu to do unsafe_div as old_virtual_price > 0.
# If A and gamma are not undergoing ramps (t < block.timestamp),
# ensure new virtual_price is not less than old virtual_price,
# else the pool suffers a loss.
if self.future_A_gamma_time < block.timestamp:
assert virtual_price > old_virtual_price, "Loss"
# -------------------------- Cache last_xcp --------------------------
self.last_xcp = xcp # geometric_mean(D * price_scale)
self.xcp_profit = xcp_profit
# ------------ Rebalance liquidity if there's enough profits to adjust it:
if virtual_price * 2 - 10**18 > xcp_profit + 2 * rebalancing_params[0]:
# allowed_extra_profit --------^
# ------------------- Get adjustment step ----------------------------
# Calculate the vector distance between price_scale and
# price_oracle.
norm: uint256 = 0
ratio: uint256 = 0
for k in range(N_COINS - 1):
ratio = unsafe_div(price_oracle[k] * 10**18, price_scale[k])
# unsafe_div because we did safediv before ----^
if ratio > 10**18:
ratio = unsafe_sub(ratio, 10**18)
else:
ratio = unsafe_sub(10**18, ratio)
norm = unsafe_add(norm, ratio**2)
norm = isqrt(norm) # <-------------------- isqrt is not in base 1e18.
adjustment_step: uint256 = max(
rebalancing_params[1], unsafe_div(norm, 5)
) # ^------------------------------------- adjustment_step.
if norm > adjustment_step: # <---------- We only adjust prices if the
# vector distance between price_oracle and price_scale is
# large enough. This check ensures that no rebalancing
# occurs if the distance is low i.e. the pool prices are
# pegged to the oracle prices.
# ------------------------------------- Calculate new price scale.
p_new: uint256[N_COINS - 1] = empty(uint256[N_COINS - 1])
for k in range(N_COINS - 1):
p_new[k] = unsafe_div(
price_scale[k] * unsafe_sub(norm, adjustment_step)
+ adjustment_step * price_oracle[k],
norm
) # <- norm is non-zero and gt adjustment_step; unsafe = safe
# ---------------- Update stale xp (using price_scale) with p_new.
xp = _xp
for k in range(N_COINS - 1):
xp[k + 1] = unsafe_div(_xp[k + 1] * p_new[k], price_scale[k])
# unsafe_div because we did safediv before ----^
# ------------------------------------------ Update D with new xp.
D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
assert D > 0 # dev: unsafe D
# Check if calculated p_new is safu:
for k in range(N_COINS):
frac: uint256 = unsafe_div(xp[k] * 10**18, D)
assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe p_new
xp[0] = unsafe_div(D, N_COINS)
for k in range(N_COINS - 1):
xp[k + 1] = D * 10**18 / (N_COINS * p_new[k]) # <---- Convert
# xp to real prices.
# ---------- Calculate new virtual_price using new xp and D. Reuse
# `old_virtual_price` (but it has new virtual_price).
old_virtual_price = unsafe_div(
10**18 * MATH.geometric_mean(xp), total_supply
) # <----- unsafe_div because we did safediv before (if vp>1e18)
# ---------------------------- Proceed if we've got enough profit.
if (
old_virtual_price > 10**18 and
2 * old_virtual_price - 10**18 > xcp_profit
):
packed_price_scale = self._pack_prices(p_new)
self.D = D
self.virtual_price = old_virtual_price
self.price_scale_packed = packed_price_scale
return packed_price_scale
# --------- price_scale was not adjusted. Update the profit counter and D.
self.D = D_unadjusted
self.virtual_price = virtual_price
return packed_price_scale
@internal
def _claim_admin_fees():
"""
@notice Claims admin fees and sends it to fee_receiver set in the factory.
@dev Functionally similar to:
1. Calculating admin's share of fees,
2. minting LP tokens,
3. admin claims underlying tokens via remove_liquidity.
"""
# --------------------- Check if fees can be claimed ---------------------
# Disable fee claiming if:
# 1. If time passed since last fee claim is less than
# MIN_ADMIN_FEE_CLAIM_INTERVAL.
# 2. Pool parameters are being ramped.
last_claim_time: uint256 = self.last_admin_fee_claim_timestamp
if (
unsafe_sub(block.timestamp, last_claim_time) < MIN_ADMIN_FEE_CLAIM_INTERVAL or
self.future_A_gamma_time > block.timestamp
):
return
xcp_profit: uint256 = self.xcp_profit # <---------- Current pool profits.
xcp_profit_a: uint256 = self.xcp_profit_a # <- Profits at previous claim.
current_lp_token_supply: uint256 = self.totalSupply
# Do not claim admin fees if:
# 1. insufficient profits accrued since last claim, and
# 2. there are less than 10**18 (or 1 unit of) lp tokens, else it can lead
# to manipulated virtual prices.
if xcp_profit <= xcp_profit_a or current_lp_token_supply < 10**18:
return
# ---------- Conditions met to claim admin fees: compute state. ----------
A_gamma: uint256[2] = self._A_gamma()
D: uint256 = self.D
vprice: uint256 = self.virtual_price
packed_price_scale: uint256 = self.price_scale_packed
fee_receiver: address = factory.fee_receiver()
balances: uint256[N_COINS] = self.balances
# Admin fees are calculated as follows.
# 1. Calculate accrued profit since last claim. `xcp_profit`
# is the current profits. `xcp_profit_a` is the profits
# at the previous claim.
# 2. Take out admin's share, which is hardcoded at 5 * 10**9.
# (50% => half of 100% => 10**10 / 2 => 5 * 10**9).
# 3. Since half of the profits go to rebalancing the pool, we
# are left with half; so divide by 2.
fees: uint256 = unsafe_div(
unsafe_sub(xcp_profit, xcp_profit_a) * self.admin_fee, 2 * 10**10
)
# ------------------------------ Claim admin fees by minting admin's share
# of the pool in LP tokens.
# This is the admin fee tokens claimed in self.add_liquidity. We add it to
# the LP token share that the admin needs to claim:
admin_share: uint256 = self.admin_lp_virtual_balance
frac: uint256 = 0
if fee_receiver != empty(address) and fees > 0:
# -------------------------------- Calculate admin share to be minted.
frac = vprice * 10**18 / (vprice - fees) - 10**18
admin_share += current_lp_token_supply * frac / 10**18
# ------ Subtract fees from profits that will be used for rebalancing.
xcp_profit -= fees * 2
# ------------------- Recalculate virtual_price following admin fee claim.
total_supply_including_admin_share: uint256 = (
current_lp_token_supply + admin_share
)
vprice = (
10**18 * self.get_xcp(D, packed_price_scale) /
total_supply_including_admin_share
)
# Do not claim fees if doing so causes virtual price to drop below 10**18.
if vprice < 10**18:
return
# ---------------------------- Update State ------------------------------
# Set admin virtual LP balances to zero because we claimed:
self.admin_lp_virtual_balance = 0
self.xcp_profit = xcp_profit
self.last_admin_fee_claim_timestamp = block.timestamp
# Since we reduce balances: virtual price goes down
self.virtual_price = vprice
# Adjust D after admin seemingly removes liquidity
self.D = D - unsafe_div(D * admin_share, total_supply_including_admin_share)
if xcp_profit > xcp_profit_a:
self.xcp_profit_a = xcp_profit # <-------- Cache last claimed profit.
# --------------------------- Handle Transfers ---------------------------
admin_tokens: uint256[N_COINS] = empty(uint256[N_COINS])
if admin_share > 0:
for i in range(N_COINS):
admin_tokens[i] = (
balances[i] * admin_share /
total_supply_including_admin_share
)
# _transfer_out tokens to admin and update self.balances. State
# update to self.balances occurs before external contract calls:
self._transfer_out(i, admin_tokens[i], fee_receiver)
log ClaimAdminFee(fee_receiver, admin_tokens)
@internal
@pure
def xp(
balances: uint256[N_COINS],
price_scale_packed: uint256,
) -> uint256[N_COINS]:
result: uint256[N_COINS] = balances
result[0] *= PRECISIONS[0]
packed_prices: uint256 = price_scale_packed
for i in range(1, N_COINS):
p: uint256 = (packed_prices & PRICE_MASK) * PRECISIONS[i]
result[i] = result[i] * p / PRECISION
packed_prices = packed_prices >> PRICE_SIZE
return result
@internal
@view
def _alpha(last_timestamp: uint256, ma_exp_time: uint256) -> uint256:
return MATH.wad_exp(
-convert(
unsafe_div(
(block.timestamp - last_timestamp) * 10**18,
ma_exp_time
),
int256,
)
)
@view
@internal
def _A_gamma() -> uint256[2]:
t1: uint256 = self.future_A_gamma_time
A_gamma_1: uint256 = self.future_A_gamma
gamma1: uint256 = A_gamma_1 & 2**128 - 1
A1: uint256 = A_gamma_1 >> 128
if block.timestamp < t1:
# --------------- Handle ramping up and down of A --------------------
A_gamma_0: uint256 = self.initial_A_gamma
t0: uint256 = self.initial_A_gamma_time
t1 -= t0
t0 = block.timestamp - t0
t2: uint256 = t1 - t0
A1 = ((A_gamma_0 >> 128) * t2 + A1 * t0) / t1
gamma1 = ((A_gamma_0 & 2**128 - 1) * t2 + gamma1 * t0) / t1
return [A1, gamma1]
@internal
@view
def _fee(xp: uint256[N_COINS]) -> uint256:
fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)
f: uint256 = MATH.reduction_coefficient(xp, fee_params[2])
return unsafe_div(
fee_params[0] * f + fee_params[1] * (10**18 - f),
10**18
)
@internal
@pure
def get_xcp(D: uint256, price_scale_packed: uint256) -> uint256:
x: uint256[N_COINS] = empty(uint256[N_COINS])
x[0] = D / N_COINS
packed_prices: uint256 = price_scale_packed # <------ No precisions here
# because we don't switch to "real" units.
for i in range(1, N_COINS):
x[i] = D * 10**18 / (N_COINS * (packed_prices & PRICE_MASK))
packed_prices = packed_prices >> PRICE_SIZE
return MATH.geometric_mean(x)
@view
@internal
def _calc_token_fee(amounts: uint256[N_COINS], xp: uint256[N_COINS]) -> uint256:
# fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts)
fee: uint256 = unsafe_div(
unsafe_mul(self._fee(xp), N_COINS),
unsafe_mul(4, unsafe_sub(N_COINS, 1))
)
S: uint256 = 0
for _x in amounts:
S += _x
avg: uint256 = unsafe_div(S, N_COINS)
Sdiff: uint256 = 0
for _x in amounts:
if _x > avg:
Sdiff += unsafe_sub(_x, avg)
else:
Sdiff += unsafe_sub(avg, _x)
return fee * Sdiff / S + NOISE_FEE
@internal
@view
def _calc_withdraw_one_coin(
A_gamma: uint256[2],
token_amount: uint256,
i: uint256,
update_D: bool,
) -> (uint256, uint256, uint256[N_COINS], uint256):
token_supply: uint256 = self.totalSupply
assert token_amount <= token_supply # dev: token amount more than supply
assert i < N_COINS # dev: coin out of range
xx: uint256[N_COINS] = self.balances
xp: uint256[N_COINS] = PRECISIONS
D0: uint256 = 0
# -------------------------- Calculate D0 and xp -------------------------
price_scale_i: uint256 = PRECISION * PRECISIONS[0]
packed_prices: uint256 = self.price_scale_packed
xp[0] *= xx[0]
for k in range(1, N_COINS):
p: uint256 = (packed_prices & PRICE_MASK)
if i == k:
price_scale_i = p * xp[i]
xp[k] = unsafe_div(xp[k] * xx[k] * p, PRECISION)
packed_prices = packed_prices >> PRICE_SIZE
if update_D: # <-------------- D is updated if pool is undergoing a ramp.
D0 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
else:
D0 = self.D
D: uint256 = D0
# -------------------------------- Fee Calc ------------------------------
# Charge fees on D. Roughly calculate xp[i] after withdrawal and use that
# to calculate fee. Precision is not paramount here: we just want a
# behavior where the higher the imbalance caused the more fee the AMM
# charges.
# xp is adjusted assuming xp[0] ~= xp[1] ~= x[2], which is usually not the
# case. We charge self._fee(xp), where xp is an imprecise adjustment post
# withdrawal in one coin. If the withdraw is too large: charge max fee by
# default. This is because the fee calculation will otherwise underflow.
xp_imprecise: uint256[N_COINS] = xp
xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply
fee: uint256 = self._unpack_3(self.packed_fee_params)[1] # <- self.out_fee.
if xp_correction < xp_imprecise[i]:
xp_imprecise[i] -= xp_correction
fee = self._fee(xp_imprecise)
dD: uint256 = unsafe_div(token_amount * D, token_supply)
D_fee: uint256 = fee * dD / (2 * 10**10) + 1 # <------- Actual fee on D.
# --------- Calculate `approx_fee` (assuming balanced state) in ith token.
# -------------------------------- We only need this for fee in the event.
approx_fee: uint256 = N_COINS * D_fee * xx[i] / D
# ------------------------------------------------------------------------
D -= (dD - D_fee) # <----------------------------------- Charge fee on D.
# --------------------------------- Calculate `y_out`` with `(D - D_fee)`.
y: uint256 = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, i)[0]
dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i
xp[i] = y
return dy, D, xp, approx_fee
# ------------------------ ERC20 functions -----------------------------------
@internal
def _approve(_owner: address, _spender: address, _value: uint256):
self.allowance[_owner][_spender] = _value
log Approval(_owner, _spender, _value)
@internal
def _transfer(_from: address, _to: address, _value: uint256):
assert _to not in [self, empty(address)]
self.balanceOf[_from] -= _value
self.balanceOf[_to] += _value
log Transfer(_from, _to, _value)
@view
@internal
def _domain_separator() -> bytes32:
if chain.id != CACHED_CHAIN_ID:
return keccak256(
_abi_encode(
EIP712_TYPEHASH,
NAME_HASH,
VERSION_HASH,
chain.id,
self,
salt,
)
)
return CACHED_DOMAIN_SEPARATOR
@external
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
"""
@dev Transfer tokens from one address to another.
@param _from address The address which you want to send tokens from
@param _to address The address which you want to transfer to
@param _value uint256 the amount of tokens to be transferred
@return bool True on successul transfer. Reverts otherwise.
"""
_allowance: uint256 = self.allowance[_from][msg.sender]
if _allowance != max_value(uint256):
self._approve(_from, msg.sender, _allowance - _value)
self._transfer(_from, _to, _value)
return True
@external
def transfer(_to: address, _value: uint256) -> bool:
"""
@dev Transfer token for a specified address
@param _to The address to transfer to.
@param _value The amount to be transferred.
@return bool True on successful transfer. Reverts otherwise.
"""
self._transfer(msg.sender, _to, _value)
return True
@external
def approve(_spender: address, _value: uint256) -> bool:
"""
@notice Allow `_spender` to transfer up to `_value` amount
of tokens from the caller's account.
@param _spender The account permitted to spend up to `_value` amount of
ca...
// [truncated — 68828 bytes total]
Read Contract
A 0xf446c1d0 → uint256
D 0x0f529ba2 → uint256
DOMAIN_SEPARATOR 0x3644e515 → bytes32
MATH 0xed6c1546 → address
adjustment_step 0x083812e5 → uint256
admin 0xf851a440 → address
admin_fee 0xfee3f7f9 → uint256
allowance 0xdd62ed3e → uint256
allowed_extra_profit 0x49fe9e77 → uint256
balanceOf 0x70a08231 → uint256
balances 0x4903b0d1 → uint256
calc_token_amount 0x3883e119 → uint256
calc_token_fee 0xcde699fa → uint256
calc_withdraw_one_coin 0x4fb08c5e → uint256
coins 0xc6610657 → address
decimals 0x313ce567 → uint8
factory 0xc45a0155 → address
fee 0xddca3f43 → uint256
fee_calc 0x572e5625 → uint256
fee_gamma 0x72d4f0e2 → uint256
fee_receiver 0xcab4d3db → address
future_A_gamma 0xf30cfad5 → uint256
future_A_gamma_time 0xf9ed9597 → uint256
gamma 0xb1373929 → uint256
get_dx 0x37ed3a7a → uint256
get_dy 0x556d6e9f → uint256
get_virtual_price 0xbb7b8b80 → uint256
initial_A_gamma 0x204fe3d5 → uint256
initial_A_gamma_time 0xe89876ff → uint256
last_prices 0x59189017 → uint256
last_timestamp 0x4d23bfa0 → uint256
last_xcp 0x175753e9 → uint256
lp_price 0x54f0f7d5 → uint256
ma_time 0x09c3da6a → uint256
mid_fee 0x92526c0c → uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
out_fee 0xee8de675 → uint256
packed_fee_params 0xe3616405 → uint256
packed_rebalancing_params 0x3dd65478 → uint256
precisions 0x3620604b → uint256[3]
price_oracle 0x68727653 → uint256
price_scale 0xa3f7cdd5 → uint256
salt 0xbfa0b133 → bytes32
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
version 0x54fd4d50 → string
virtual_price 0x0c46b72a → uint256
xcp_ma_time 0x99f6bdda → uint256
xcp_oracle 0x23c6afea → uint256
xcp_profit 0x7ba1a74d → uint256
xcp_profit_a 0x0b7b594b → uint256
Write Contract 18 functions
These functions modify contract state and require a wallet transaction to execute.
add_liquidity 0x4515cef3
uint256[3] amounts
uint256 min_mint_amount
returns: uint256
add_liquidity 0x75b96abc
uint256[3] amounts
uint256 min_mint_amount
address receiver
returns: uint256
apply_new_parameters 0x75f66026
uint256 _new_mid_fee
uint256 _new_out_fee
uint256 _new_fee_gamma
uint256 _new_allowed_extra_profit
uint256 _new_adjustment_step
uint256 _new_ma_time
uint256 _new_xcp_ma_time
approve 0x095ea7b3
address _spender
uint256 _value
returns: bool
exchange 0x5b41b908
uint256 i
uint256 j
uint256 dx
uint256 min_dy
returns: uint256
exchange 0xa64833a0
uint256 i
uint256 j
uint256 dx
uint256 min_dy
address receiver
returns: uint256
exchange_received 0x29b244bb
uint256 i
uint256 j
uint256 dx
uint256 min_dy
returns: uint256
exchange_received 0x767691e7
uint256 i
uint256 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_gamma 0x5e248072
uint256 future_A
uint256 future_gamma
uint256 future_time
remove_liquidity 0xecb586a5
uint256 _amount
uint256[3] min_amounts
returns: uint256[3]
remove_liquidity 0x2da5dc21
uint256 _amount
uint256[3] min_amounts
address receiver
returns: uint256[3]
remove_liquidity_one_coin 0xf1dc3cc9
uint256 token_amount
uint256 i
uint256 min_amount
returns: uint256
remove_liquidity_one_coin 0x0fbcee6e
uint256 token_amount
uint256 i
uint256 min_amount
address receiver
returns: uint256
set_admin_fee 0x3217902f
uint256 _admin_fee
stop_ramp_A_gamma 0x244c7c2e
No parameters
transfer 0xa9059cbb
address _to
uint256 _value
returns: bool
transferFrom 0x23b872dd
address _from
address _to
uint256 _value
returns: bool
Recent Transactions
No transactions found for this address