Address Contract Partially Verified
Address
0x57E69699381a651Fb0BBDBB31888F5D655Bf3f06
Balance
0 ETH
Nonce
2
Code Size
23869 bytes
Creator
0x55555555...0417 at tx 0x787fb78e...70d6ca
Indexed Transactions
0
Contract Bytecode
23869 bytes
0x60806040526004361015610011575f80fd5b5f5f3560e01c80628cc2621461382457806302ce728f146137f657806306b6f7e91461377757806306fdde031461365a57806308a0c375146135c257806308c7e366146135a55780630cab937d146130395780630df8dfac1461301057806313966db514612ff2578063146a29eb14612fd25780631c6c959714612f515780632164e85b14612f335780632b3ba681146110be5780632cdacb5014612eee5780632f86556814612a9f57806333fd6f7414612a4c5780633f2617cb146129585780633f4ba83a146128fd57806342f43d0b146128b8578063476343ee14612644578063487413761461257a5780634ac8eb5f1461255f5780634cae6513146124e55780634fd422df146124c157806354fd4d501461249757806356ecf28b1461247957806357d775f81461243e5780635e43c47b1461240957806360c52d05146123c05780636551f16c146123365780636b091695146122725780636f307dc31461222d578063730a7514146121a2578063757991a81461213b578063759cb53b1461210c57806375a410141461208c57806378e97925146120515780637adbf97314611f635780637b10399914611f1e5780637e92968414611bbb5780637ec4b57114611b7a5780638049d97114611afd5780638214ba4814611a835780638285ef4014611a5457806383d4433914611a1b5780638456cb591461199657806386993bef146119785780638cad7fbe146119395780638da5cb5b146109d85780638f50ea921461191b57806391ebebbd146118bc57806392bbcaed1461189e57806393ae0df91461187757806393f46f6414611831578063945c91421461180257806395d14ca8146117c05780639a295e731461177a5780639fe34bdc14611158578063a053db68146112af578063a36a363014611291578063ab7cfaf914611273578063acb70815146111f5578063b5af3062146111d1578063b5b5454714611198578063b68d0a091461115d578063b78294dd14611158578063b95c57461461113a578063c00007b0146110e1578063c0a7e892146110be578063c3192f14146110c3578063c49bb2d1146110be578063ca51bb59146110a0578063cacf3b5814611074578063cadac47914610f86578063cdd72d5214610f2c578063d41ddc9614610dee578063d619658b14610dd2578063d6bda0c014610bf4578063d870ce6614610b5a578063d8dfeb4514610b15578063e509b9d914610ad4578063e551d11d14610ab6578063e69bc27114610a71578063e7a3317414610a1d578063f2f4eb26146109d8578063f301af4214610982578063f384bd0514610964578063f5c7f89914610927578063f8112eed146108e6578063fbbbf94c146108ac578063fd6d0526146104325763fff5d9da1461040d575f80fd5b3461042f578060031936011261042f5760206104276140e3565b604051908152f35b80fd5b503461042f57608036600319011261042f5761044c6139b8565b90606435906024356001600160a01b0383168084036108a85761046d61413a565b60405163e8673ef560e01b81526020816004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa90811561089d57849161085f575b506001600160a01b03163303610850578015908115610846575b5061083757600e548110610828576104ea6144c4565b505050604435670de0b6b3a764000003670de0b6b3a764000081116108145761051c670de0b6b3a76400009183613d37565b0493670de0b6b3a764000061053d6105348785613b9c565b600d5490613d37565b04946105498684613b9c565b90610552613c9f565b6001600160801b03815116838181119182156107ff575b50506107f0576001600160801b03610586818516828451166140c3565b169081815264e8d4a5100082029180830464e8d4a5100014901517156107525760208101916001600160801b0383511611610766575b51905160801b6001600160801b0319166001600160801b039190911617601755670de0b6b3a764000090610608905b6105f789601954613d68565b601955610602614158565b90613d37565b0495610613876157d2565b7f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03169561064a908890886158d3565b6001600160a01b037f00000000000000000000000086f3188e24a6902c6d4d7b820eabdc4255f636e416620186a088810290898204148915171561075257813b1561074e57869160248392604051948593849263140e25ad60e31b845260048401525af180156107435761072a575b5060408051948552602085018890528401526060830152600192916001600160a01b03909116907f76cd0cedf979345ca241ce6de696a520a8efc860c6c10d9db2a7953307c237fc90608090a255604080516001600160a01b039092168252602082019290925290819081015b0390f35b610735868092613a58565b61073f575f6106b9565b8480fd5b6040513d88823e3d90fd5b8680fd5b634e487b7160e01b87526011600452602487fd5b61076e615150565b600254600181018091116107dc576001600160801b036106089381670de0b6b3a76400009694846105eb956002557febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3358d80a28164e8d4a51000818551160416835294965050935050506105bc565b634e487b7160e01b88526011600452602488fd5b6316c6823f60e01b8652600486fd5b6108099250613b9c565b600f5411835f610569565b634e487b7160e01b83526011600452602483fd5b6362171e8f60e01b8252600482fd5b631e4ec46b60e01b8252600482fd5b905030145f6104d4565b63d520e5ad60e01b8352600483fd5b90506020813d602011610895575b8161087a60209383613a58565b810103126108915761088b90613d23565b5f6104ba565b8380fd5b3d915061086d565b6040513d86823e3d90fd5b8280fd5b503461042f578060031936011261042f57601554601654604080516001600160a01b038416815260a09390931c6020840152820152606090f35b503461042f57602036600319011261042f576109206109036139b8565b61091361090e61495a565b613f6f565b61091b61413a565b615726565b6001815580f35b503461042f57604036600319011261042f576001906109586109476139b8565b61094f61413a565b60243590615002565b55602060405160018152f35b503461042f578060031936011261042f576020600954604051908152f35b503461042f57602036600319011261042f576004359060015482101561042f5760606109ad83613b64565b506001815491015460ff6040519260018060a01b038116845260a01c16151560208301526040820152f35b503461042f578060031936011261042f576040517f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03168152602090f35b503461042f57602036600319011261042f57610a63337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b610a6e60043561491b565b80f35b503461042f57604036600319011261042f576040610a8d6139ce565b9160043581526004602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600a54604051908152f35b503461042f57602036600319011261042f576020906001600160a01b03610af96139b8565b16815260088252604060018060a01b0391205416604051908152f35b503461042f578060031936011261042f576040517f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03168152602090f35b503461042f57602036600319011261042f57600435610ba3337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b68056bc75e2d631000008110610be5576020817febc3aac103344d02f2e1b3fb50ca740929143c84082dd6e7698963e902aa636992600e55604051908152a180f35b630309cb8760e51b8252600482fd5b503461042f57606036600319011261042f57602435610c116139e4565b610c1961413a565b610c2233614290565b6001600160a01b03811615610dc357610c396144c4565b505050610c44614158565b5081610cd3575b610c609150610c5b600435614729565b61529f565b906040610c6b613c71565b01610c77815133614320565b15610c8b5760208360018455604051908152f35b60649250610cad610c9a613c9f565b338452601b6020526040842054906143c2565b90338352601a6020526040832054905191633b49de0f60e21b8452600452602452604452fd5b602082610d05610d2c9430337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61523b565b604051636e553f6560e01b8152600481019190915230602482015292839081906044820190565b0381867f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03165af1918215610db8578392610d7e575b50610d79610c6092339030614bd4565b610c4b565b91506020823d602011610db0575b81610d9960209383613a58565b81010312610dac57905190610d79610d69565b5f80fd5b3d9150610d8c565b6040513d85823e3d90fd5b631e4ec46b60e01b8352600483fd5b503461042f578060031936011261042f576020604051600f8152f35b503461042f57604036600319011261042f57600435610e0b6139ce565b610e1361413a565b610e1c33614290565b6001600160a01b03168015610dc357610e336144c4565b505050338352601b6020526040832054610f1e575b610e53333084614644565b604051635d043b2960e11b815260048101929092526024820152306044820152602081606481857f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03165af18015610f1357610ee4575b506040610ebc613c71565b01610ec8815133614320565b15610ed557506001815580f35b90606491610cad610c9a613c9f565b610f059060203d602011610f0c575b610efd8183613a58565b810190613ce9565b505f610eb1565b503d610ef3565b6040513d84823e3d90fd5b610f26614158565b50610e48565b503461042f578060031936011261042f57610f45613ba9565b506080610f5061402d565b915091506001600160801b0360208183511692015116610f6e613dcd565b91604051938452602084015260408301526060820152f35b503461042f57604036600319011261042f57600435610fa36139ce565b90610fac61413a565b6001600160a01b03821615610dc357906020610ff992610fca6144c4565b505050610d058130337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61523b565b0381867f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03165af18015610db8578390611040575b610920925030614bd4565b506020823d60201161106c575b8161105a60209383613a58565b81010312610dac576109209151611035565b3d915061104d565b503461042f578060031936011261042f5761072661109061402d565b9060409492945194859485613a88565b503461042f578060031936011261042f576020601c54604051908152f35b613ae2565b503461042f578060031936011261042f576020601854604051908152f35b503461042f57602036600319011261042f576110fb6139b8565b61110361413a565b6001600160a01b0381811683526008602052604083205416801561112a5761092091614e9b565b508061113591614e9b565b610920565b503461042f578060031936011261042f576020600154604051908152f35b613b30565b503461042f57602036600319011261042f5760406111796139b8565b61118b61118582613fac565b91613f11565b9082519182526020820152f35b503461042f57602036600319011261042f576020906040906001600160a01b036111c06139b8565b168152600383522054604051908152f35b503461042f57602036600319011261042f5760206104276111f06139b8565b613fac565b503461042f57604036600319011261042f576004356112126139ce565b61121a61413a565b6001600160a01b03811615610dc35760209261126a60019261123a6144c4565b505050611245613c9f565b61124f86826143c2565b9561126261125c88614729565b91614729565b90339261475a565b55604051908152f35b503461042f578060031936011261042f576020600d54604051908152f35b503461042f578060031936011261042f576020600c54604051908152f35b503461042f5760a036600319011261042f576112c96139b8565b60243591906064356044356084356001600160401b03811161073f573660238201121561073f5780600401356112fe81613b4d565b9161130c6040519384613a58565b818352602083016024819360051b8301019136831161177657602401905b82821061175e5750505061133c61413a565b61134533614290565b859061134f6144c4565b50505061135a614158565b506001600160a01b0386168088526012602052604088205490969060ff161561174f576001600160a01b0361138e85613c1c565b517f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03169116819003611739575083515f198101908111611725576001600160a01b03906113e39086613c29565b517f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03169291168290036116e9578561163e575b61142b90610c5b8b614729565b93604051926370a0823160e01b8452306004850152602084602481865afa938415611633578a946115ff575b50883b156115fb57604051634b4ecc5560e11b8152336004820152602481018c9052608060448201529151608483018190528a90839060a4820190835b8181106115d6575050819293503060648301520381838c5af180156115cb579089916115b2575b5050906020602492604051938480926370a0823160e01b82523060048301525afa80156115a7578890611573575b6114f39250613b9c565b9380851061155c5750836115119161150c338330614bd4565b613d68565b9560405194855260208501526040840152606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a26040610c6b613c71565b633b5d56ed60e11b87526004526024849052604486fd5b506020823d60201161159f575b8161158d60209383613a58565b81010312610dac576114f391516114e9565b3d9150611580565b6040513d8a823e3d90fd5b816115bc91613a58565b6115c757875f6114bb565b8780fd5b6040513d8b823e3d90fd5b85516001600160a01b031683526020958601958f955087945090920191600101611494565b8980fd5b9093506020813d60201161162b575b8161161b60209383613a58565b81010312610dac5751925f611457565b3d915061160e565b6040513d8c823e3d90fd5b925061166c8530337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61523b565b604051636e553f6560e01b8152600481018690523060248201526020816044818c865af180156115cb5789906116b5575b61142b91506116ad338230614bd4565b93905061141e565b506020813d6020116116e1575b816116cf60209383613a58565b81010312610dac5761142b905161169d565b3d91506116c2565b8489838251925f198401938411610814576044936001600160a01b03916117109190613c29565b51169063b0b3262d60e01b8352600452602452fd5b634e487b7160e01b89526011600452602489fd5b60449089906001600160a01b0361171088613c1c565b631311dc6d60e01b8852600488fd5b6020809161176b846139fa565b81520191019061132a565b8880fd5b503461042f578060031936011261042f576080906040519050620186a08152620186a06020820152670de0b6b3a76400006040820152670de0b6b3a76400006060820152f35b503461042f578060031936011261042f576060601454604051906001600160401b03811682526001600160401b038160401c16602083015260801c6040820152f35b503461042f578060031936011261042f57602060405173d533a949740bb3306d119cc777fa900ba034cd528152f35b503461042f5760209061184336613b04565b9192509015611861576104279161185861402d565b925050506146e3565b6118729161186d613c9f565b6146e3565b610427565b503461042f578060031936011261042f57602061042761189561402d565b925050506151f9565b503461042f578060031936011261042f576020600f54604051908152f35b503461042f57606036600319011261042f576118d66139ce565b60406118e06139e4565b9260043581526005602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600e54604051908152f35b503461042f57602036600319011261042f5760209060ff906040906001600160a01b036119646139b8565b168152601284522054166040519015158152f35b503461042f578060031936011261042f576020601d54604051908152f35b503461042f578060031936011261042f576119db337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b600a54806119e7575080f35b601e5580600a557fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f63866020604051838152a180f35b503461042f57602036600319011261042f576020906040906001600160a01b03611a436139b8565b168152600783522054604051908152f35b503461042f578060031936011261042f5760406017548151906001600160801b038116825260801c6020820152f35b503461042f57602036600319011261042f577fc98711414b05c67ac27ffb415026d97ad5094c5490747891a834cb44f64940d96020600435611aef337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b80600f55604051908152a180f35b503461042f57604036600319011261042f57611b176139ce565b611b1f61413a565b611b2833614290565b6001600160a01b0381161561083757611b6290611b436144c4565b505050338352601b6020526040832054611b6c575b3390600435614644565b6040610ebc613c71565b611b74614158565b50611b58565b503461042f57602090611b8c36613b04565b9192509015611baa5761042791611ba161402d565b92505050614460565b61187291611bb6613c9f565b614460565b503461042f57602036600319011261042f57600435611c04337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b81601d54828103611c3f575b507fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5602083604051908152a180f35b604051631526fe2760e01b815260048101919091527f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03169060c081602481855afa908115610db8578391611ee9575b506040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa91821561089d578492611eb2575b5081611d95575b5090606460209260405194859384926321d0683360e11b84528860048501526024840152600160448401525af18015610db857611d37575b50601d819055817fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5611c10565b6020813d602011611d8d575b81611d5060209383613a58565b810103126108a8577fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e591611d85602092613cf8565b509150611d0a565b3d9150611d43565b60209060446040959394955180948193636197390160e11b83528760048401528160248401525af18015611e7057611e7b575b506040516370a0823160e01b81523060048201526020816024817f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03165afa8015611e705782918691611e3b575b5010611e2c579083915f611cd2565b6346bc68ed60e01b8452600484fd5b9150506020813d602011611e68575b81611e5760209383613a58565b81010312610dac578190515f611e1d565b3d9150611e4a565b6040513d87823e3d90fd5b6020813d602011611eaa575b81611e9460209383613a58565b8101031261073f57611ea590613cf8565b611dc8565b3d9150611e87565b935090506020833d602011611ee1575b81611ecf60209383613a58565b81010312610dac57849251905f611ccb565b3d9150611ec2565b611f0b915060c03d60c011611f17575b611f038183613a58565b810190613d75565b5050925050505f611c96565b503d611ef9565b503461042f578060031936011261042f576040517f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03168152602090f35b503461042f57602036600319011261042f576040611f7f6139b8565b611fb3337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b612021611fbe613c71565b805184516001600160a01b03918216815290841660208201529092907fb9d23ad01dc54c1fad84c770708fbe314a369b505c073949334dad786229805e90604090a16001600160a01b0316808352601580546001600160a01b0319169091179055565b6020810151601580546001600160a01b031660a09290921b6001600160a01b031916919091179055015160165580f35b503461042f578060031936011261042f5760206040517f0000000000000000000000000000000000000000000000000000000067d220008152f35b503461042f57602036600319011261042f576120a66139b8565b6120ae61413a565b3380835260086020908152604080852080546001600160a01b0319166001600160a01b039590951694851790555192835290917ff4239ad0860f93469699dd4be8040b8838c5e25bb6cf24a1dfb381b937ff078c9190a26001815580f35b503461042f578060031936011261042f576020604051734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8152f35b503461042f578060031936011261042f57602061042761217b7f0000000000000000000000000000000000000000000000000000000067d2200042613b9c565b7f0000000000000000000000000000000000000000000000000000000000093a8090613d4a565b503461042f57602036600319011261042f576004356121eb337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b620186a08111610be5577f1f93e1cc6afaef2ddc4d7ef7b0f45ac3278d548338c839e9be18695732a662746040600c548151908152836020820152a1600c5580f35b503461042f578060031936011261042f576040517f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e6001600160a01b03168152602090f35b503461042f57604036600319011261042f5761228c6139b8565b6122946139ce565b61229c61413a565b6001600160a01b0382163303612309576001600160a01b038116156122c45761092091614e9b565b60405162461bcd60e51b815260206004820152601760248201527f66776420616464726573732063616e6e6f7420626520300000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606490fd5b503461042f57602036600319011261042f5760043561237f337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b670de0b6b3a76400008111610be5576020817f3e0c428758b3ad6ab4fd85e8257e4eee404fb36e80c0251143635b549cd70b8892600d55604051908152a180f35b503461042f57604036600319011261042f576123da6139ce565b6123e261413a565b6001600160a01b0381161561083757610920906123fd6144c4565b50505060043533614bd4565b503461042f57602036600319011261042f576109206124266139b8565b61243161090e61495a565b61243961413a565b614a26565b503461042f578060031936011261042f5760206040517f0000000000000000000000000000000000000000000000000000000000093a808152f35b503461042f578060031936011261042f576020600254604051908152f35b503461042f578060031936011261042f576060906040519060038252602082015260026040820152f35b503461042f57602036600319011261042f5760206104276124e06139b8565b613f11565b503461042f57602036600319011261042f577f18e1a8f58cc03bc99f69c27336072db255c3f01827f2923f654ddc209e2b8db56020600435612551337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b80601055604051908152a180f35b503461042f578060031936011261042f576020610427613dcd565b503461042f57604036600319011261042f576125946139b8565b61259c613a79565b6125d0337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b612634575b601154604080516001600160a01b038084168252841660208201529192917f74cd8ef76f78382ae0f3ee4e21117be974af0041a24dc98f7aa8ddabb0b2960c9190a16001600160a01b03166001600160a01b0319919091161760115580f35b61263c6144c4565b5050506125d5565b503461042f578060031936011261042f5761265d61413a565b6126656144c4565b5050604051630abca72960e41b81527f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03169150602081600481855afa908115610db857839161287e575b506040516379bd9e4160e01b81526001600160a01b03821690602081600481855afa908115611e7057859161284c575b5061271561217b7f0000000000000000000000000000000000000000000000000000000067d2200042613b9c565b90601c54821190811591612841575b5061283257601c5583601854936019549382601855826019556127478587613d68565b823b15610891576040516340c10f1960e01b81526001600160a01b0392909216600483015260248201529082908290604490829084905af18015610f135761281d575b5050803b15610891576040516362f2221960e01b8152836004820152826024820152848160448183865af18015611e7057612808575b506040937f9cc800ba322ea82ab3e1e911dd4ccd84129687c4952f4ee2f937e3ac68755131606060019387519081528660208201528588820152a15582519182526020820152f35b612813858092613a58565b610891575f6127c0565b8161282791613a58565b61089157835f61278a565b630e1b248d60e01b8552600485fd5b90508114155f612724565b90506020813d602011612876575b8161286760209383613a58565b81010312610dac57515f6126e7565b3d915061285a565b90506020813d6020116128b0575b8161289960209383613a58565b810103126108a8576128aa90613d23565b5f6126b7565b3d915061288c565b503461042f578060031936011261042f576040517f00000000000000000000000086f3188e24a6902c6d4d7b820eabdc4255f636e46001600160a01b03168152602090f35b503461042f578060031936011261042f57612942337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b600a541561294d5780f35b610a6e601e5461491b565b503461042f57604036600319011261042f576129726139b8565b61297a613a79565b90337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b0316148015612a1a575b15612a0b577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da59160409160018060a01b03169081855260126020528285209015159060ff1981541660ff831617905582519182526020820152a180f35b631d1e647b60e01b8352600483fd5b50337f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316146129ae565b503461042f57604036600319011261042f576040612a686139b8565b91612a716139ce565b9260018060a01b031681526006602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f57602036600319011261042f57612ab96139b8565b612ac161413a565b60405163d25adeb360e01b8152906020826004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa918215610db8578392612eb2575b506001600160a01b0382169133839003612ea3576001600160a01b0382168015612e9457612b3b6144c4565b505050612b46614158565b612b4f84614290565b612b598185614320565b612e8557612b65613c9f565b90828752601a602052604087205490838852601b602052612b896040892054614729565b926001600160801b0384169260208201926001600160801b03845116155f14612dee57612bbf670de0b6b3a76400009186613d37565b04600c54620186a0019081620186a011612dda576001600160801b0398969492620186a0612bf6612c749d9c9a9896948c94613d37565b0481811115612dd057509a8b955b877f158ba9ab7bbbd08eeffa4753bad41f4d450e24831d293427308badf3eadd8c766060612c3a612c358a896143c2565b614729565b9d8e6040519b8c528a60208d015216998a6040820152a2612c5a8a614d34565b82612c688c828751166140c3565b168452828551166140c3565b168252848b52601b602052612c8d8360408d2054613b9c565b858c52601b6020528060408d20558015159081612dbb575b50612dac5751905160801b6001600160801b0319166001600160801b03919091161760175588969594612d119490939092909188917fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040919082519182526020820152a386614644565b813b156108a857604051632be71c5f60e11b81526001600160a01b037f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd2289166004820152602481018590526001600160801b0391909116604482015291908290606490829084905af18015610db857612d93575b50600160209255604051908152f35b612d9e838092613a58565b612da8575f612d84565b5080fd5b631abfe8a760e01b8b5260048bfd5b612dc69150826143c2565b601054115f612ca5565b90509a8b95612c04565b634e487b7160e01b8c52601160045260248cfd5b612e16612e056001600160801b0385511687613d37565b6001600160801b0386511690613d4a565b8b80612e55575b612e35575b670de0b6b3a764000091612bbf91613d37565b6001810180911115612e2257634e487b7160e01b8c52601160045260248cfd5b5085612e7f612e6e6001600160801b0388511684613d37565b6001600160801b0387511690613d4a565b10612e1d565b633af2cafd60e11b8652600486fd5b631e4ec46b60e01b8552600485fd5b6387e9041360e01b8452600484fd5b9091506020813d602011612ee6575b81612ece60209383613a58565b810103126108a857612edf90613d23565b905f612b0f565b3d9150612ec1565b503461042f578060031936011261042f576040517f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03168152602090f35b503461042f578060031936011261042f576020601954604051908152f35b503461042f57602036600319011261042f576004358015158103612da857612f77613d05565b50610726604051612f8781613a0e565b5f81525f6020820152612f9861413a565b83612fa16144c4565b96915094612fba575b6001905560405194859485613a88565b9150506018546001612fca613c9f565b929050612faa565b503461042f578060031936011261042f57602060405164e8d4a510008152f35b503461042f578060031936011261042f576020600b54604051908152f35b503461042f578060031936011261042f576011546040516001600160a01b039091168152602090f35b5034610dac576080366003190112610dac576130536139b8565b60443591906064356024356001600160401b038211610dac5736602383011215610dac578160040135926001600160401b038411610dac576024830192602436918660051b010111610dac576130a761413a565b6130b033614290565b6130b86144c4565b5050506130c3614158565b506130cc613c9f565b9260018060a01b03821694855f52601260205260ff60405f20541615613596578015613582576130fb82613cd5565b6001600160a01b037f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22898116911681900361357957505f198101928184116135655761314f61314a858486613cc5565b613cd5565b6001600160a01b037f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec811695911685900361353357506001600160801b0386511615613524576131a190339086614644565b6040516370a0823160e01b815230600482015291602083602481875afa9283156134b7575f936134f0575b50863b15610dac57604051634b4ecc5560e11b815233600482015260248101869052608060448201526084810183905291829160a48301915f5b8181106134c25750505090805f923060648301520381838a5af180156134b7576134a2575b506040516370a0823160e01b8152306004820152602081602481865afa80156133ee578290889061346c575b6132619250613b9c565b968088106134555750856001600160801b03855116155f14613411575086935b338752601b60205260408720548086116133f9575b506132b7906132a489614729565b339130916132b189614729565b9161475a565b6040516370a0823160e01b8152306004820152602081602481865afa9081156133ee57908793929184916133b5575b50906132f191613b9c565b80613340575b505050604051928352602083015283604083015260608201527f6b2b212085c14b2dc48ec56d19c05084e5d8ef233539cdd3dc0d70a4ec7f4f9e60803392a26040610c6b613c71565b60405163a9059cbb60e01b815233600482015260248101919091529160209183916044918391905af18015611e705761337c575b8085916132f7565b6020813d6020116133ad575b8161339560209383613a58565b8101031261073f576133a690613cf8565b505f613374565b3d9150613388565b919350506020813d6020116133e6575b816133d260209383613a58565b81010312610dac57518692906132f16132e6565b3d91506133c5565b6040513d89823e3d90fd5b945096506132b761340a85896143c2565b9790613296565b9361343d61342c6001600160801b036020840151168a613d37565b6001600160801b0383511690613d4a565b94156132815793600181018091116107525793613281565b633b5d56ed60e11b87526004526024879052604486fd5b50506020813d60201161349a575b8161348760209383613a58565b81010312610dac57816132619151613257565b3d915061347a565b6134af9196505f90613a58565b5f945f61322b565b6040513d5f823e3d90fd5b9193509160019060209081906001600160a01b036134df886139fa565b168152019401910191849392613206565b9092506020813d60201161351c575b8161350c60209383613a58565b81010312610dac5751915f6131cc565b3d91506134ff565b631abfe8a760e01b5f5260045ffd5b905061314a9161354293613cc5565b63b0b3262d60e01b5f9081526004929092526001600160a01b0316602452604490fd5b634e487b7160e01b5f52601160045260245ffd5b61354283613cd5565b634e487b7160e01b5f52603260045260245ffd5b631311dc6d60e01b5f5260045ffd5b34610dac575f366003190112610dac576020601054604051908152f35b34610dac576020366003190112610dac5760043561360a337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b620186a0811161364b577fe796e9ae748449310fcd1cc6718aab236c9b8d2e0e04dacb232ba564d5b338cc60406009548151908152836020820152a1600955005b630309cb8760e51b5f5260045ffd5b34610dac575f366003190112610dac576040515f90601354918260011c6001841693841561376d575b60208210851461375957818452602084019490811561373e57506001146136e0575b50906136b5816040930382613a58565b8151928391602083525180918160208501528484015e5f828201840152601f01601f19168101030190f35b91905060135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090915f905b808210613724575090915081016020016136b56136a5565b91926001816020925483858801015201910190929161370c565b60ff1916855250151560051b820160200190506136b56136a5565b634e487b7160e01b5f52602260045260245ffd5b90607f1690613683565b34610dac576020366003190112610dac576004356137bf337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3d565b7f388cba11a61d4b31467a533c88eef75abfb6097ff2ab4652d086560fffcc2f836040600b548151908152836020820152a1600b55005b34610dac575f366003190112610dac5761380e61413a565b6020613818614158565b60015f55604051908152f35b34610dac576020366003190112610dac5761383d6139b8565b61384561413a565b61384e81614d34565b60015461385a81613bc1565b916001600160a01b03165f5b8281106139415760015f5583515f19810190859082116135655761388982613bc1565b9160015b818111156138ef57836040518091602082016020835281518091526020604084019201905f5b8181106138c1575050500390f35b825180516001600160a01b0316855260209081015181860152869550604090940193909201916001016138b3565b60206138fb8285613c29565b510151905f1981018181116135655760019260206139198389613c29565b510152613939838060a01b0361392f8488613c29565b5151169187613c29565b51520161388d565b8061394d600192613b64565b5054828060a01b0381169081159081156139aa575b506139a257805f52600660205260405f20845f5260205260405f2054602061398a8489613c29565b5101526139978287613c29565b515201915b91613866565b50019161399c565b60ff915060a01c1687613962565b600435906001600160a01b0382168203610dac57565b602435906001600160a01b0382168203610dac57565b604435906001600160a01b0382168203610dac57565b35906001600160a01b0382168203610dac57565b604081019081106001600160401b03821117613a2957604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117613a2957604052565b90601f801991011681019081106001600160401b03821117613a2957604052565b602435908115158203610dac57565b926001600160801b0391959460209183604060c09660e089019a89526001600160401b03815116868a01526001600160401b038682015116828a0152015116606087015260808601528281511660a0860152015116910152565b34610dac575f366003190112610dac576020604051670de0b6b3a76400008152f35b6060906003190112610dac57600435906024358015158103610dac57906044358015158103610dac5790565b34610dac575f366003190112610dac576020604051620186a08152f35b6001600160401b038111613a295760051b60200190565b6001548110156135825760015f81815291901b7fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60191565b9190820391821161356557565b60405190613bb682613a0e565b5f6020838281520152565b90613bcb82613b4d565b613bd86040519182613a58565b8281528092613be9601f1991613b4d565b01905f5b828110613bf957505050565b602090604051613c0881613a0e565b5f81525f8382015282828501015201613bed565b8051156135825760200190565b80518210156135825760209160051b010190565b15613c4457565b60405162461bcd60e51b815260206004820152600560248201526421636f726560d81b6044820152606490fd5b60405190613c7e82613a3d565b6015546001600160a01b038116835260a01c60208301526016546040830152565b60405190613cac82613a0e565b6017546001600160801b038116835260801c6020830152565b91908110156135825760051b0190565b356001600160a01b0381168103610dac5790565b90816020910312610dac575190565b51908115158203610dac57565b60405190613d1282613a3d565b5f6040838281528260208201520152565b51906001600160a01b0382168203610dac57565b8181029291811591840414171561356557565b8115613d54570490565b634e487b7160e01b5f52601260045260245ffd5b9190820180921161356557565b91908260c0910312610dac57613d8a82613d23565b91613d9760208201613d23565b91613da460408301613d23565b91613db160608201613d23565b91613dca60a0613dc360808501613d23565b9301613cf8565b90565b601d548015613eba57604051631526fe2760e01b8152600481019190915260c0816024817f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165afa9081156134b7576024916020915f91613e95575b506040516370a0823160e01b815230600482015292839182906001600160a01b03165afa9081156134b7575f91613e66575090565b90506020813d602011613e8d575b81613e8160209383613a58565b81010312610dac575190565b3d9150613e74565b613eae915060c03d60c011611f1757611f038183613a58565b5050925050505f613e31565b506040516370a0823160e01b81523060048201526020816024817f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896001600160a01b03165afa9081156134b7575f91613e66575090565b6001600160a01b03165f908152601b6020908152604080832054600254600390935292205491929190808210613f45575050565b909264e8d4a510006001915b04930192818414613f6a5764e8d4a51000600191613f51565b925050565b15613f7657565b60405162461bcd60e51b815260206004820152600e60248201526d10b932bbb0b93226b0b730b3b2b960911b6044820152606490fd5b613fb461413a565b613fbd81614290565b6001600160a01b03165f908152601a6020526040902054613fdc613dcd565b9081811115613fee57505b9060015f55565b9050613fe7565b6040519061400282613a3d565b8160406014546001600160401b03811683526001600160401b0381831c16602084015260801c910152565b5f614036613d05565b5060405161404381613a0e565b5f81525f602082015250614055613ff5565b9061405f826154f7565b8051909190156140b457506060810151926001600160401b0360208301511660208401526001600160801b03604083015116604084015260806140ae6018546001600160801b03871690613d68565b92015190565b92905060185490613dca613c9f565b906001600160801b03809116911603906001600160801b03821161356557565b600a54801561412d576001600160801b036140fc61402d565b92505050511690670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561356557613dca91613d4a565b50670de0b6b3a764000090565b60025f54146141495760025f55565b633ee5aeb560e01b5f5260045ffd5b614160613c71565b80516040516315caaba160e21b81526001600160a01b037f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd228981166004830152929392909160209183916024918391165afa9081156134b7575f9161425e575b508015613d54576ec097ce7bc90715b34b9f10000000000491604081019083825181036141eb57505050565b6bffffffffffffffffffffffff4290811660208481019190915291909352905160a09290921b6001600160a01b0319166001600160a01b039092169190911760155560168390556040518381527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a49190a1565b90506020813d602011614288575b8161427960209383613a58565b81010312610dac57515f6141bf565b3d915061426c565b61429981614d34565b6001600160a01b037f00000000000000000000000086f3188e24a6902c6d4d7b820eabdc4255f636e481165f90815260066020908152604080832094909316808352938152828220805490839055601a9091529190205490620186a090048082106143175761430791613b9c565b905b5f52601a60205260405f2055565b50505f90614309565b906009549182156143ba57614356614336613c9f565b6001600160a01b039092165f818152601b602052604090205490926143c2565b9081156143b1575f52601a60205260405f20549182156143a95761437991613d37565b90620186a0820291808304620186a0149015171561356557670de0b6b3a76400006143a49204613d4a565b111590565b505050505f90565b50505050600190565b505050600190565b6020810180516001600160801b03166143db575050905b565b9092916144066143f56001600160801b0386511683613d37565b6001600160801b0384511690613d4a565b936001600160801b0381511615159283614433575b50505061442457565b90600181018091116135655790565b61445792935061444e6001600160801b038092511687613d37565b91511690613d4a565b105f808061441b565b909160208201916001600160801b03835116155f1461447f5750505090565b61449b612e6e6001600160801b03839796949597511684613d37565b94836144b0575b836144335750505061442457565b81516001600160801b0316151593506144a2565b5f905f906144d0613d05565b506144d9613ff5565b906144e3826154f7565b80511515806144f0575050565b9194509250926143d96001600160801b03602060806060870151968287016001600160401b0381511690604089019186835116907f6b9ef8676ff86d806b7a7bd7a9b0266910c9fce560c26289d7ed7cd7743127c786888701926001600160401b038451169460408901958c875116916040519384528c84015260408301526060820152a17f939dcec711228d083924a0db6cecbb66bb8403ea7cdbfe2f928901cbac2cdfc160408d6001600160401b0384511682519182528a820152a151916001600160401b0383169052519186831690526001600160401b034216808a526fffffffffffffffff00000000000000006014549260401b169187191617176014558460145491811990821990861b161691161760145561461388601854613d68565b601855015182815116831960175416176017550151166001600160801b036017549181199060801b16911617601755565b9160207fba68c7a8c8efbddb7e938ee32ecc28a68930c18105a3a0ab9563eac7f051cf5a9160018060a01b031693845f52601a825260405f20614688828254613b9c565b9055614693816157d2565b6001600160a01b03841693813086036146b2575b5050604051908152a3565b6146dc917f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd22896158d3565b5f816146a7565b90916001600160801b03825116155f146146fc57505090565b9092916020840161471a612e6e6001600160801b0383511684613d37565b94836144335750505061442457565b6001600160801b038111614743576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd5b9391909161476781614d34565b6001600160801b0361477c84828851166140c3565b16855260208501906001600160801b0361479984828551166140c3565b16825260018060a01b031694855f52601b6020526147c66001600160801b0360405f205494168094613b9c565b865f52601b6020528060405f20558015159081614906575b506135245751905160801b6001600160801b0319166001600160801b0391909116176017556001600160a01b0383169283614850575b507fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040916001600160801b038351921682526020820152a3565b917f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b0316803b15610dac57604051632770a7eb60e21b81526001600160a01b039490941660048501526001600160801b03821660248501525f908490604490829084905af19182156134b7577fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0936040936148f6575b50915091614814565b5f61490091613a58565b5f6148ed565b6149119150826143c2565b601054115f6147de565b6001600160801b03811161364b576020817fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f638692600a55604051908152a1565b337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614801561498f5790565b5060405163670fb82160e01b81526020816004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa9081156134b7575f916149ec575b506001600160a01b0316331490565b90506020813d602011614a1e575b81614a0760209383613a58565b81010312610dac57614a1890613d23565b5f6149dd565b3d91506149fa565b6001600160a01b038116903082148015614bcc575b8015614bbc575b614b8257815f52600760205260405f2054155f14614b4e57600154600f811015614b1b5768010000000000000000811015613a2957806001614a879201600155613b64565b50826bffffffffffffffffffffffff60a01b825416179055600154825f52600760205260405f2055817fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2803b15614b1157506143d9906040519063a9059cbb60e01b60208301523060248301525f604483015260448252614b0c606483613a58565b615cd8565b6143d99150615726565b60405162461bcd60e51b815260206004820152600b60248201526a6d6178207265776172647360a81b6044820152606490fd5b50805f52600760205260405f20545f19810190811161356557614b7090613b64565b5080546001600160a01b031615614b86575b5050565b80546001600160a01b031916821790557fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2565b50614bc68161590f565b15614a42565b508115614a3b565b9160018060a01b031691825f52601a60205260405f20614bf5838254613d68565b905581306001600160a01b03831603614d01575b5050601d5480614c42575b5060207f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391604051908152a2565b6040516321d0683360e11b8152600481019190915260248101829052600160448201526020816064815f7f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165af180156134b75715614c14576020813d602011614cf9575b81614cbc60209383613a58565b81010312610dac577f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391614cf1602092613cf8565b509150614c14565b3d9150614caf565b614d2d9130907f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd228961523b565b5f81614c09565b5f907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b757614e86575b506002549060015490835b5f198110614daa575050505050565b836001600160a01b03831680614e3d575b865b858110614e16575085821015614e0d57808752601b60205264e8d4a51000604088205404818852601b60205260408820556001820180921161075257865260036020526040862055600101614d9b565b50505050505050565b80614e2e89876001948b889c999d9e9a979b98615980565b01919490939796929591614dbd565b8087526003602052604087205491508582141580614e72575b15614dbb57905084908087526003602052856040882055614dbb565b50808752601b602052604087205415614e56565b614e939192505f90613a58565b5f905f614d90565b7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b031691823b15610dac57604051633bd73ee360e21b81525f816024818380983060048401525af180156134b757614fed575b506002549160015492845b5f198110614f1057505050505050565b816001600160a01b03841680614fa4575b875b878110614f7d575083821015614f7357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc57875260036020526040872055600101614f00565b5050505050505050565b80614f95888860019489889d999e9f9b979c98615980565b01929691959094989792614f23565b8088526003602052604088205491508382141580614fd9575b15614f2157905082908088526003602052836040892055614f21565b50808852601b602052604088205415614fbd565b614ffa9193505f90613a58565b5f915f614ef5565b5f91907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b75761513b575b506002549060015491845b84811061507957505050505050565b816001600160a01b038416806150f2575b875b8681106150dc575083821015614f7357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc5787526003602052604087205560010161506a565b806150ec8a886001948988615980565b0161508c565b8088526003602052604088205491508382141580615127575b1561508a5790508290808852600360205283604089205561508a565b50808852601b60205260408820541561510b565b6151489193505f90613a58565b5f915f61505f565b5f7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b7576151e6575b5060025460015491905f1982106151c157505050565b815b8381106151d05750505050565b806151e084806001948680615980565b016151c3565b6151f291505f90613a58565b5f5f6151ab565b600a5490516001600160801b0316808211156152335761521891613b9c565b6001600160801b03811115613dca57506001600160801b0390565b50505f615218565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526143d991614b0c608483613a58565b906001600160801b03809116911601906001600160801b03821161356557565b9190916152aa613c9f565b92601054916001600160801b03811692831061352457600b54620186a00180620186a011613565576152e0620186a09185613d37565b046152ea866151f9565b8181106154e2575085516001600160801b031661548357805b61536b82976001600160801b036020820191816153446153268883875116613d68565b8261533c6153338a614729565b8287511661527f565b168452614729565b1692839052516001600160801b0391161660809190911b6001600160801b03191617601755565b335f52601b60205260405f20615382838254613d68565b9055848082111561547a5761539691613b9c565b915b82615466575b7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac576040516340c10f1960e01b81526001600160a01b03861660048201526001600160801b039290921660248301525f908290604490829084905af180156134b757615456575b506040519384526020840152604083015260018060a01b0316907f10a0132d3bf8c82a7fb93a86160f3074ca5c3e5706fa2bcdf0e2b5fd495af09b60603392a3565b5f61546091613a58565b5f615414565b61547283601954613d68565b60195561539e565b50505f91615398565b6020860190806154ca6154b46154a36001600160801b0386511684613d37565b6001600160801b038b511690613d4a565b936001600160801b0361444e818c511687613d37565b10156153035790600181018091116135655790615303565b63586ce21f60e11b5f5260045260245260445ffd5b9060405160a081018181106001600160401b03821117613a29576040525f8152602081015f815260408201935f855260608301915f8352608084019360405161553f81613a0e565b5f81525f6020820152855280966001600160401b038451164211615566575b505050505050565b60016155fd9252615575613c9f565b8652604061558d6001600160401b0386511642613b9c565b6011549582015182516356fb8f2d60e01b81526001600160a01b037f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd228981166004830152602482018490526001600160801b0390921660448201529196919485929190911690829081906064820190565b03915afa80156134b7575f925f916156bf575b50670de0b6b3a7640000946001600160401b03615657959481946001600160801b0361564e951690521684526001600160801b038851511690613d37565b91511690613d37565b04808252801515908161569e575b5015615695576156866001600160801b03809251169251928284511661527f565b1690525b5f808080808061555e565b5f91505261568a565b6001600160801b0391506156b790828551511690613d68565b11155f615665565b939250506040833d60401161571e575b816156dc60409383613a58565b81010312610dac578251926001600160401b0384168403610dac5760200151906001600160801b0382168203610dac5791929190670de0b6b3a7640000615610565b3d91506156cf565b6001600160a01b03165f8181526007602052604090205480615746575050565b5f1981019081116135655761575a90613b64565b5080546001600160a01b03168290036157a15780546001600160a01b03191690557f646cfe9445aed85f4853d501d1924d2bdabb1bbf12531df29f929f07ba4169e05f80a2565b60405162461bcd60e51b8152602060048201526009602482015268042dad2e6dac2e8c6d60bb1b6044820152606490fd5b601d54806157de575050565b604051631526fe2760e01b8152600481019190915260c0816024817f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165afa80156134b7575f9260209284926158a9575b50604051636197390160e11b815260048101919091526024810184905292839160449183916001600160a01b03165af180156134b7576158745750565b6020813d6020116158a1575b8161588d60209383613a58565b81010312610dac5761589e90613cf8565b50565b3d9150615880565b60449192506158c69060c03d60c011611f1757611f038183613a58565b5050925050509190615837565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526143d991614b0c606483613a58565b6001600160a01b03908116907f000000000000000000000000c33aa628b10655b36eaa7ee880d6bc4789dd228916811461597b577f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03161461597757600190565b5f90565b505f90565b93929161598c90613b64565b5080546001600160a01b03169290831561555e576040516370a0823160e01b815230600482015293602085602481845afa9485156134b7575f95615ca4575b508496600183019687549582149686888099615c96575b80615c8d575b615c0c575b50505f8281526004602090815260408083206001600160a01b03878116855292529091205495169384615a3e575b50505050505081615a33575b50615a30575050565b55565b90508214155f615a27565b825f52600560205260405f2060018060a01b0385165f5260205260405f20855f5260205260405f20548860018060a01b03841692831580158091615c03575b615a8b575b50505050615a1b565b82615bfb575b5081615beb575b5015615b8b57615ae690855f52600660205260405f20875f5260205269021e19e0c9bab2400000615adf60405f205492895f52601b60205261060260405f2054918c613b9c565b0490613d68565b9182615b28575b5050505b5f52600560205260405f209060018060a01b03165f5260205260405f20905f5260205260405f20555f808080808080888180615a82565b90615b5183615b82959c93875f52600660205260405f20895f526020525f6040812055876158d3565b84867fce405e67b4d6e56e438257e15f160ae28b450e6e7659bbc4c1f4e09a1ac846cb6020604051878152a4613b9c565b965f8080615aed565b9050615bcf9150835f52600660205260405f20855f5260205269021e19e0c9bab2400000615adf60405f205492875f52601b60205261060260405f2054918a613b9c565b825f52600660205260405f20845f5260205260405f2055615af1565b60ff91505460a01c16155f615a98565b91505f615a91565b50898410615a7d565b615c1591613b9c565b69021e19e0c9bab240000081029080820469021e19e0c9bab2400000149015171561356557615c4a9060175460801c90613d4a565b8015615c8357825f52600460205260405f2060018060a01b0385165f52602052615c7960405f20918254613d68565b90555b5f866159ed565b5097508497615c7c565b508082116159e8565b5060175460801c15156159e2565b9094506020813d602011615cd0575b81615cc060209383613a58565b81010312610dac5751935f6159cb565b3d9150615cb3565b905f602091828151910182855af1156134b7575f513d615d2757506001600160a01b0381163b155b615d075750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415615d0056fea164736f6c634300081c000a
Verified Source Code Partial Match
Compiler: v0.8.28+commit.7893614a
EVM: cancun
Optimization: Yes (200 runs)
ResupplyPair.sol 450 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ResupplyPair
* @notice Based on code from Drake Evans and Frax Finance's lending pair contract (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ResupplyPairConstants } from "./pair/ResupplyPairConstants.sol";
import { ResupplyPairCore } from "./pair/ResupplyPairCore.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { VaultAccount, VaultAccountingLibrary } from "../libraries/VaultAccount.sol";
import { IRateCalculator } from "../interfaces/IRateCalculator.sol";
import { ISwapper } from "../interfaces/ISwapper.sol";
import { IFeeDeposit } from "../interfaces/IFeeDeposit.sol";
import { IResupplyRegistry } from "../interfaces/IResupplyRegistry.sol";
import { IConvexStaking } from "../interfaces/IConvexStaking.sol";
import { EpochTracker } from "../dependencies/EpochTracker.sol";
contract ResupplyPair is ResupplyPairCore, EpochTracker {
using VaultAccountingLibrary for VaultAccount;
using SafeERC20 for IERC20;
using SafeCast for uint256;
uint256 public lastFeeEpoch;
address public constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
// Staking Info
address public immutable convexBooster;
uint256 public convexPid;
error FeesAlreadyDistributed();
error IncorrectStakeBalance();
/// @param _core Core contract address
/// @param _configData config data
/// @param _immutables immutable data
/// @param _customConfigData extras
constructor(
address _core,
bytes memory _configData,
bytes memory _immutables,
bytes memory _customConfigData
) ResupplyPairCore(_core, _configData, _immutables, _customConfigData) EpochTracker(_core) {
(, address _govToken, address _convexBooster, uint256 _convexpid) = abi.decode(
_customConfigData,
(string, address, address, uint256)
);
//add gov token rewards
_insertRewardToken(_govToken);
//convex info
if(_convexBooster != address(0)){
convexBooster = _convexBooster;
convexPid = _convexpid;
//approve
collateral.forceApprove(convexBooster, type(uint256).max);
//add rewards for curve staking
_insertRewardToken(CRV);
_insertRewardToken(CVX);
emit SetConvexPool(_convexpid);
}
}
// ============================================================================================
// Functions: Helpers
// ============================================================================================
function getConstants()
external
pure
returns (
uint256 _LTV_PRECISION,
uint256 _LIQ_PRECISION,
uint256 _EXCHANGE_PRECISION,
uint256 _RATE_PRECISION
)
{
_LTV_PRECISION = LTV_PRECISION;
_LIQ_PRECISION = LIQ_PRECISION;
_EXCHANGE_PRECISION = EXCHANGE_PRECISION;
_RATE_PRECISION = RATE_PRECISION;
}
/// @notice The ```getUserSnapshot``` function gets user level accounting data
/// @param _address The user address
/// @return _borrowShares The user borrow shares
/// @return _collateralBalance The user collateral balance
function getUserSnapshot(
address _address
) external returns (uint256 _borrowShares, uint256 _collateralBalance) {
_collateralBalance = userCollateralBalance(_address);
_borrowShares = userBorrowShares(_address);
}
/// @notice The ```getPairAccounting``` function gets all pair level accounting numbers
/// @return _claimableFees Total claimable fees
/// @return _totalBorrowAmount Total borrows
/// @return _totalBorrowShares Total borrow shares
/// @return _totalCollateral Total collateral
function getPairAccounting()
external
view
returns (
uint256 _claimableFees,
uint128 _totalBorrowAmount,
uint128 _totalBorrowShares,
uint256 _totalCollateral
)
{
VaultAccount memory _totalBorrow;
(, , _claimableFees, _totalBorrow) = previewAddInterest();
_totalBorrowAmount = _totalBorrow.amount;
_totalBorrowShares = _totalBorrow.shares;
_totalCollateral = totalCollateral();
}
/// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares
/// @param _amount Amount of borrow
/// @param _roundUp Whether to roundup during division
/// @param _previewInterest Whether to simulate interest accrual
/// @return _shares The number of shares
function toBorrowShares(
uint256 _amount,
bool _roundUp,
bool _previewInterest
) external view returns (uint256 _shares) {
if (_previewInterest) {
(, , , VaultAccount memory _totalBorrow) = previewAddInterest();
_shares = _totalBorrow.toShares(_amount, _roundUp);
} else {
_shares = totalBorrow.toShares(_amount, _roundUp);
}
}
/// @notice The ```toBorrowAmount``` function converts a given amount of borrow debt into the number of shares
/// @param _shares Shares of borrow
/// @param _roundUp Whether to roundup during division
/// @param _previewInterest Whether to simulate interest accrual
/// @return _amount The amount of asset
function toBorrowAmount(
uint256 _shares,
bool _roundUp,
bool _previewInterest
) external view returns (uint256 _amount) {
if (_previewInterest) {
(, , , VaultAccount memory _totalBorrow) = previewAddInterest();
_amount = _totalBorrow.toAmount(_shares, _roundUp);
} else {
_amount = totalBorrow.toAmount(_shares, _roundUp);
}
}
// ============================================================================================
// Functions: Configuration
// ============================================================================================
/// @notice The ```SetOracleInfo``` event is emitted when the oracle info is set
/// @param oldOracle The old oracle address
/// @param newOracle The new oracle address
event SetOracleInfo(
address oldOracle,
address newOracle
);
/// @notice The ```setOracleInfo``` function sets the oracle data
/// @param _newOracle The new oracle address
function setOracle(address _newOracle) external onlyOwner{
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
emit SetOracleInfo(
_exchangeRateInfo.oracle,
_newOracle
);
_exchangeRateInfo.oracle = _newOracle;
exchangeRateInfo = _exchangeRateInfo;
}
/// @notice The ```SetMaxLTV``` event is emitted when the max LTV is set
/// @param oldMaxLTV The old max LTV
/// @param newMaxLTV The new max LTV
event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV);
/// @notice The ```setMaxLTV``` function sets the max LTV
/// @param _newMaxLTV The new max LTV
function setMaxLTV(uint256 _newMaxLTV) external onlyOwner{
if (_newMaxLTV > LTV_PRECISION) revert InvalidParameter();
emit SetMaxLTV(maxLTV, _newMaxLTV);
maxLTV = _newMaxLTV;
}
/// @notice The ```SetRateCalculator``` event is emitted when the rate contract is set
/// @param oldRateCalculator The old rate contract
/// @param newRateCalculator The new rate contract
event SetRateCalculator(address oldRateCalculator, address newRateCalculator);
/// @notice The ```setRateCalculator``` function sets the rate contract address
/// @param _newRateCalculator The new rate contract address
/// @param _updateInterest Whether to update interest before setting new rate calculator
function setRateCalculator(address _newRateCalculator, bool _updateInterest) external onlyOwner{
//should add interest before changing rate calculator
//however if there is an intrinsic problem with the current rate calculate, need to be able
//to update without calling addInterest
if(_updateInterest){
_addInterest();
}
emit SetRateCalculator(address(rateCalculator), _newRateCalculator);
rateCalculator = IRateCalculator(_newRateCalculator);
}
/// @notice The ```SetLiquidationFees``` event is emitted when the liquidation fees are set
/// @param oldLiquidationFee The old clean liquidation fee
/// @param newLiquidationFee The new clean liquidation fee
event SetLiquidationFees(
uint256 oldLiquidationFee,
uint256 newLiquidationFee
);
/// @notice The ```setLiquidationFees``` function sets the liquidation fees
/// @param _newLiquidationFee The new clean liquidation fee
function setLiquidationFees(
uint256 _newLiquidationFee
) external onlyOwner{
if (_newLiquidationFee > LIQ_PRECISION) revert InvalidParameter();
emit SetLiquidationFees(
liquidationFee,
_newLiquidationFee
);
liquidationFee = _newLiquidationFee;
}
/// @notice The ```SetMintFees``` event is emitted when the liquidation fees are set
/// @param oldMintFee The old mint fee
/// @param newMintFee The new mint fee
event SetMintFees(
uint256 oldMintFee,
uint256 newMintFee
);
/// @notice The ```setMintFees``` function sets the mint
/// @param _newMintFee The new mint fee
function setMintFees(
uint256 _newMintFee
) external onlyOwner{
emit SetMintFees(
mintFee,
_newMintFee
);
mintFee = _newMintFee;
}
function setBorrowLimit(uint256 _limit) external onlyOwner{
_setBorrowLimit(_limit);
}
/// @notice The ```SetBorrowLimit``` event is emitted when the borrow limit is set
/// @param limit The new borrow limit
event SetBorrowLimit(uint256 limit);
function _setBorrowLimit(uint256 _limit) internal {
if(_limit > type(uint128).max){
revert InvalidParameter();
}
borrowLimit = _limit;
emit SetBorrowLimit(_limit);
}
event SetMinimumRedemption(uint256 min);
function setMinimumRedemption(uint256 _min) external onlyOwner{
if(_min < 100 * PAIR_DECIMALS ){
revert InvalidParameter();
}
minimumRedemption = _min;
emit SetMinimumRedemption(_min);
}
event SetMinimumLeftover(uint256 min);
function setMinimumLeftoverDebt(uint256 _min) external onlyOwner{
minimumLeftoverDebt = _min;
emit SetMinimumLeftover(_min);
}
event SetMinimumBorrowAmount(uint256 min);
function setMinimumBorrowAmount(uint256 _min) external onlyOwner{
minimumBorrowAmount = _min;
emit SetMinimumBorrowAmount(_min);
}
event SetProtocolRedemptionFee(uint256 fee);
/// @notice Sets the redemption fee percentage for this specific pair
/// @dev The fee is 1e18 precision (1e16 = 1%) and taken from redemptions and sent to the protocol.
/// @param _fee The new redemption fee percentage. Must be less than or equal to 1e18 (100%)
function setProtocolRedemptionFee(uint256 _fee) external onlyOwner{
if(_fee > EXCHANGE_PRECISION) revert InvalidParameter();
protocolRedemptionFee = _fee;
emit SetProtocolRedemptionFee(_fee);
}
/// @notice The ```WithdrawFees``` event fires when the fees are withdrawn
/// @param recipient To whom the assets were sent
/// @param interestFees the amount of interest based fees claimed
/// @param otherFees the amount of other fees claimed(mint/redemption)
event WithdrawFees(address recipient, uint256 interestFees, uint256 otherFees);
/// @notice The ```withdrawFees``` function withdraws fees accumulated
/// @return _fees the amount of interest based fees claimed
/// @return _otherFees the amount of other fees claimed(mint/redemption)
function withdrawFees() external nonReentrant returns (uint256 _fees, uint256 _otherFees) {
// Accrue interest if necessary
_addInterest();
//get deposit contract
address feeDeposit = IResupplyRegistry(registry).feeDeposit();
uint256 lastDistributedEpoch = IFeeDeposit(feeDeposit).lastDistributedEpoch();
uint256 currentEpoch = getEpoch();
//current epoch must be greater than last claimed epoch
//current epoch must be equal to the FeeDeposit prev distributed epoch (FeeDeposit must distribute first)
if(currentEpoch <= lastFeeEpoch || currentEpoch != lastDistributedEpoch){
revert FeesAlreadyDistributed();
}
lastFeeEpoch = currentEpoch;
//get fees and clear
_fees = claimableFees;
_otherFees = claimableOtherFees;
claimableFees = 0;
claimableOtherFees = 0;
//mint new stables to the receiver
IResupplyRegistry(registry).mint(feeDeposit,_fees+_otherFees);
//inform deposit contract of this pair's contribution
IFeeDeposit(feeDeposit).incrementPairRevenue(_fees,_otherFees);
emit WithdrawFees(feeDeposit, _fees, _otherFees);
}
/// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted
/// @param swapper The swapper address
/// @param approval The approval
event SetSwapper(address swapper, bool approval);
/// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address
/// @dev
/// @param _swapper The swapper address
/// @param _approval The approval
function setSwapper(address _swapper, bool _approval) external{
if(msg.sender == owner() || msg.sender == registry){
swappers[_swapper] = _approval;
emit SetSwapper(_swapper, _approval);
}else{
revert OnlyProtocolOrOwner();
}
}
/// @notice The ```SetConvexPool``` event fires when convex pool id is updated
/// @param pid the convex pool id
event SetConvexPool(uint256 pid);
/// @notice The ```setConvexPool``` function is called update the underlying convex pool
/// @dev
/// @param pid the convex pool id
function setConvexPool(uint256 pid) external onlyOwner{
_updateConvexPool(pid);
emit SetConvexPool(pid);
}
function _updateConvexPool(uint256 _pid) internal{
uint256 currentPid = convexPid;
if(currentPid != _pid){
//get previous staking
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
//get balance
uint256 stakedBalance = IConvexStaking(_rewards).balanceOf(address(this));
if(stakedBalance > 0){
//withdraw
IConvexStaking(_rewards).withdrawAndUnwrap(stakedBalance,false);
if(collateral.balanceOf(address(this)) < stakedBalance){
revert IncorrectStakeBalance();
}
}
//stake in new pool
IConvexStaking(convexBooster).deposit(_pid, stakedBalance, true);
//update pid
convexPid = _pid;
}
}
function _stakeUnderlying(uint256 _amount) internal override{
uint256 currentPid = convexPid;
if(currentPid != 0){
IConvexStaking(convexBooster).deposit(currentPid, _amount, true);
}
}
function _unstakeUnderlying(uint256 _amount) internal override{
uint256 currentPid = convexPid;
if(currentPid != 0){
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
IConvexStaking(_rewards).withdrawAndUnwrap(_amount, false);
}
}
function totalCollateral() public view override returns(uint256 _totalCollateralBalance){
uint256 currentPid = convexPid;
if(currentPid != 0){
//get staking
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
//get balance
_totalCollateralBalance = IConvexStaking(_rewards).balanceOf(address(this));
}else{
_totalCollateralBalance = collateral.balanceOf(address(this));
}
}
// ============================================================================================
// Functions: Access Control
// ============================================================================================
uint256 previousBorrowLimit;
/// @notice The ```pause``` function is called to pause all contract functionality
function pause() external onlyOwner{
if (borrowLimit > 0) {
previousBorrowLimit = borrowLimit;
_setBorrowLimit(0);
}
}
/// @notice The ```unpause``` function is called to unpause all contract functionality
function unpause() external onlyOwner{
if (borrowLimit == 0) _setBorrowLimit(previousBorrowLimit);
}
}
ERC20.sol 311 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
ReentrancyGuard.sol 87 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
SafeCast.sol 1162 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
ResupplyPairConstants.sol 32 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ResupplyPairConstants
* @notice Based on code from Drake Evans and Frax Finance's pair constants (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
abstract contract ResupplyPairConstants {
// Precision settings
uint256 public constant LTV_PRECISION = 1e5; // 5 decimals
uint256 public constant LIQ_PRECISION = 1e5;
uint256 public constant EXCHANGE_PRECISION = 1e18;
uint256 public constant RATE_PRECISION = 1e18;
uint256 public constant SHARE_REFACTOR_PRECISION = 1e12;
uint256 public constant PAIR_DECIMALS = 1e18;
error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate);
error BorrowerSolvent();
error InsufficientDebtAvailable(uint256 _assets, uint256 _request);
error SlippageTooHigh(uint256 _minOut, uint256 _actual);
error BadSwapper();
error InvalidReceiver();
error InvalidLiquidator();
error InvalidRedemptionHandler();
error InvalidParameter();
error InvalidPath(address _expected, address _actual);
error InsufficientDebtToRedeem();
error MinimumRedemption();
error InsufficientBorrowAmount();
error OnlyProtocolOrOwner();
}
ResupplyPairCore.sol 1252 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ResupplyPairCore
* @notice Based on code from Drake Evans and Frax Finance's lending pair core contract (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ResupplyPairConstants } from "./ResupplyPairConstants.sol";
import { VaultAccount, VaultAccountingLibrary } from "../../libraries/VaultAccount.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IOracle } from "../../interfaces/IOracle.sol";
import { IRateCalculator } from "../../interfaces/IRateCalculator.sol";
import { ISwapper } from "../../interfaces/ISwapper.sol";
import { IResupplyRegistry } from "../../interfaces/IResupplyRegistry.sol";
import { ILiquidationHandler } from "../../interfaces/ILiquidationHandler.sol";
import { RewardDistributorMultiEpoch } from "../RewardDistributorMultiEpoch.sol";
import { WriteOffToken } from "../WriteOffToken.sol";
import { IERC4626 } from "../../interfaces/IERC4626.sol";
import { CoreOwnable } from "../../dependencies/CoreOwnable.sol";
import { IMintable } from "../../interfaces/IMintable.sol";
abstract contract ResupplyPairCore is CoreOwnable, ResupplyPairConstants, RewardDistributorMultiEpoch {
using VaultAccountingLibrary for VaultAccount;
using SafeERC20 for IERC20;
using SafeCast for uint256;
//forked fraxlend at version 3,0,0
function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) {
_major = 3;
_minor = 0;
_patch = 2;
}
// ============================================================================================
// Settings set by constructor()
// ============================================================================================
// Asset and collateral contracts
address public immutable registry;
IERC20 internal immutable debtToken;
IERC20 public immutable collateral;
IERC20 public immutable underlying;
// LTV Settings
/// @notice The maximum LTV allowed for this pair
/// @dev 1e5 precision
uint256 public maxLTV;
//max borrow
uint256 public borrowLimit;
//Fees
/// @notice The liquidation fee, given as a % of repayment amount
/// @dev 1e5 precision
uint256 public mintFee;
uint256 public liquidationFee;
/// @dev 1e18 precision
uint256 public protocolRedemptionFee;
uint256 public minimumRedemption = 100 * PAIR_DECIMALS; //minimum amount of debt to redeem
uint256 public minimumLeftoverDebt = 10000 * PAIR_DECIMALS; //minimum amount of assets left over via redemptions
uint256 public minimumBorrowAmount = 1000 * PAIR_DECIMALS; //minimum amount of assets to borrow
// Interest Rate Calculator Contract
IRateCalculator public rateCalculator; // For complex rate calculations
// Swapper
mapping(address => bool) public swappers; // approved swapper addresses
// Metadata
string public name;
// ============================================================================================
// Storage
// ============================================================================================
/// @notice Stores information about the current interest rate
CurrentRateInfo public currentRateInfo;
struct CurrentRateInfo {
uint64 lastTimestamp;
uint64 ratePerSec;
uint128 lastShares;
}
/// @notice Stores information about the current exchange rate. Collateral:Asset ratio
/// @dev Struct packed to save SLOADs. Amount of Collateral Token to buy 1e18 Asset Token
ExchangeRateInfo public exchangeRateInfo;
struct ExchangeRateInfo {
address oracle;
uint96 lastTimestamp;
uint256 exchangeRate;
}
// Contract Level Accounting
VaultAccount public totalBorrow; // amount = total borrow amount with interest accrued, shares = total shares outstanding
uint256 public claimableFees; //amount of interest gained that is claimable as fees
uint256 public claimableOtherFees; //amount of redemption/mint fees claimable by protocol
WriteOffToken immutable public redemptionWriteOff; //token to keep track of redemption write offs
// User Level Accounting
/// @notice Stores the balance of collateral for each user
mapping(address => uint256) internal _userCollateralBalance; // amount of collateral each user is backed
/// @notice Stores the balance of borrow shares for each user
mapping(address => uint256) internal _userBorrowShares; // represents the shares held by individuals
// ============================================================================================
// Constructor
// ============================================================================================
/// @notice The ```constructor``` function is called on deployment
/// @param _core Core contract address
/// @param _configData abi.encode(address _collateral, address _oracle, address _rateCalculator, uint256 _maxLTV, uint256 _borrowLimit, uint256 _liquidationFee, uint256 _mintFee, uint256 _protocolRedemptionFee)
/// @param _immutables abi.encode(address _registry)
/// @param _customConfigData abi.encode(address _name, address _govToken, address _underlyingStaking, uint256 _stakingId)
constructor(
address _core,
bytes memory _configData,
bytes memory _immutables,
bytes memory _customConfigData
) CoreOwnable(_core){
(address _registry) = abi.decode(
_immutables,
(address)
);
registry = _registry;
debtToken = IERC20(IResupplyRegistry(registry).token());
{
(
address _collateral,
address _oracle,
address _rateCalculator,
uint256 _maxLTV,
uint256 _initialBorrowLimit,
uint256 _liquidationFee,
uint256 _mintFee,
uint256 _protocolRedemptionFee
) = abi.decode(
_configData,
(address, address, address, uint256, uint256, uint256, uint256, uint256)
);
// Pair Settings
collateral = IERC20(_collateral);
if(IERC20Metadata(_collateral).decimals() != 18){
revert InvalidParameter();
}
underlying = IERC20(IERC4626(_collateral).asset());
if(IERC20Metadata(address(underlying)).decimals() != 18){
revert InvalidParameter();
}
// approve so this contract can deposit
underlying.forceApprove(_collateral, type(uint256).max);
currentRateInfo.lastShares = uint128(IERC4626(_collateral).convertToShares(PAIR_DECIMALS));
exchangeRateInfo.oracle = _oracle;
rateCalculator = IRateCalculator(_rateCalculator);
borrowLimit = _initialBorrowLimit;
//Liquidation Fee Settings
liquidationFee = _liquidationFee;
mintFee = _mintFee;
protocolRedemptionFee = _protocolRedemptionFee;
// set maxLTV
maxLTV = _maxLTV;
}
//starting reward types
redemptionWriteOff = new WriteOffToken(address(this));
_insertRewardToken(address(redemptionWriteOff));//add redemption token as a reward
//set the redemption token as non claimable via getReward
rewards[0].is_non_claimable = true;
{
(string memory _name,,,) = abi.decode(
_customConfigData,
(string, address, address, uint256)
);
// Metadata
name = _name;
// Instantiate Interest
_addInterest();
// Instantiate Exchange Rate
_updateExchangeRate();
}
}
// ============================================================================================
// Helpers
// ============================================================================================
//get total collateral, either parked here or staked
function totalCollateral() public view virtual returns(uint256 _totalCollateralBalance);
function userBorrowShares(address _account) public view returns(uint256 borrowShares){
borrowShares = _userBorrowShares[_account];
uint256 globalEpoch = currentRewardEpoch;
uint256 userEpoch = userRewardEpoch[_account];
if(userEpoch < globalEpoch){
//need to calculate shares while keeping this as a view function
for(;;){
//reduce shares by refactoring amount
borrowShares /= SHARE_REFACTOR_PRECISION;
unchecked {
userEpoch += 1;
}
if(userEpoch == globalEpoch){
break;
}
}
}
}
//get _userCollateralBalance minus redemption tokens
function userCollateralBalance(address _account) public nonReentrant returns(uint256 _collateralAmount){
_syncUserRedemptions(_account);
_collateralAmount = _userCollateralBalance[_account];
//since there are some very small dust during distribution there could be a few wei
//in user collateral that is over total collateral. clamp to total
uint256 total = totalCollateral();
_collateralAmount = _collateralAmount > total ? total : _collateralAmount;
}
/// @notice The ```totalDebtAvailable``` function returns the total balance of debt tokens in the contract
/// @return The balance of debt tokens held by contract
function totalDebtAvailable() external view returns (uint256) {
(,,, VaultAccount memory _totalBorrow) = previewAddInterest();
return _totalDebtAvailable(_totalBorrow);
}
/// @notice The ```_totalDebtAvailable``` function returns the total amount of debt that can be issued on this pair
/// @param _totalBorrow Total borrowed amount, inclusive of interest
/// @return The amount of debt that can be issued
function _totalDebtAvailable(VaultAccount memory _totalBorrow) internal view returns (uint256) {
uint256 _borrowLimit = borrowLimit;
uint256 borrowable = _borrowLimit > _totalBorrow.amount ? _borrowLimit - _totalBorrow.amount : 0;
return borrowable > type(uint128).max ? type(uint128).max : borrowable;
}
function currentUtilization() external view returns (uint256) {
uint256 _borrowLimit = borrowLimit;
if(_borrowLimit == 0){
return PAIR_DECIMALS;
}
(,,, VaultAccount memory _totalBorrow) = previewAddInterest();
return _totalBorrow.amount * PAIR_DECIMALS / _borrowLimit;
}
/// @notice The ```_isSolvent``` function determines if a given borrower is solvent given an exchange rate
/// @param _borrower The borrower address to check
/// @param _exchangeRate The exchange rate, i.e. the amount of collateral to buy 1e18 asset
/// @return Whether borrower is solvent
function _isSolvent(address _borrower, uint256 _exchangeRate) internal view returns (bool) {
uint256 _maxLTV = maxLTV;
if (_maxLTV == 0) return true;
//must look at borrow shares of current epoch so user helper function
//user borrow shares should be synced before _isSolvent is called
uint256 _borrowerAmount = totalBorrow.toAmount(_userBorrowShares[_borrower], true);
if (_borrowerAmount == 0) return true;
//anything that calls _isSolvent will call _syncUserRedemptions beforehand
uint256 _collateralAmount = _userCollateralBalance[_borrower];
if (_collateralAmount == 0) return false;
uint256 _ltv = ((_borrowerAmount * _exchangeRate * LTV_PRECISION) / EXCHANGE_PRECISION) / _collateralAmount;
return _ltv <= _maxLTV;
}
function _isSolventSync(address _borrower, uint256 _exchangeRate) internal returns (bool){
//checkpoint rewards and sync _userCollateralBalance
_syncUserRedemptions(_borrower);
return _isSolvent(_borrower, _exchangeRate);
}
// ============================================================================================
// Modifiers
// ============================================================================================
/// @notice Checks for solvency AFTER executing contract code
/// @param _borrower The borrower whose solvency we will check
modifier isSolvent(address _borrower) {
//checkpoint rewards and sync _userCollateralBalance before doing other actions
_syncUserRedemptions(_borrower);
_;
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
if (!_isSolvent(_borrower, _exchangeRateInfo.exchangeRate)) {
revert Insolvent(
totalBorrow.toAmount(_userBorrowShares[_borrower], true),
_userCollateralBalance[_borrower], //_issolvent sync'd so take base _userCollateral
_exchangeRateInfo.exchangeRate
);
}
}
// ============================================================================================
// Reward Implementation
// ============================================================================================
function _isRewardManager() internal view override returns(bool){
return msg.sender == address(core) || msg.sender == IResupplyRegistry(registry).rewardHandler();
}
function _fetchIncentives() internal override{
IResupplyRegistry(registry).claimRewards(address(this));
}
function _totalRewardShares() internal view override returns(uint256){
return totalBorrow.shares;
}
function _userRewardShares(address _account) internal view override returns(uint256){
return _userBorrowShares[_account];
}
function _increaseUserRewardEpoch(address _account, uint256 _currentUserEpoch) internal override{
//convert shares to next epoch shares
//share refactoring will never be 0
_userBorrowShares[_account] = _userBorrowShares[_account] / SHARE_REFACTOR_PRECISION;
//update user reward epoch
userRewardEpoch[_account] = _currentUserEpoch + 1;
}
function earned(address _account) public override returns(EarnedData[] memory claimable){
EarnedData[] memory earneddata = super.earned(_account);
uint256 rewardCount = earneddata.length - 1;
claimable = new EarnedData[](rewardCount);
//remove index 0 as we dont need to report the write off tokens
for (uint256 i = 1; i <= rewardCount; ) {
claimable[i-1].amount = earneddata[i].amount;
claimable[i-1].token = earneddata[i].token;
unchecked{ i += 1; }
}
}
function _checkAddToken(address _address) internal view virtual override returns(bool){
if(_address == address(collateral)) return false;
if(_address == address(debtToken)) return false;
return true;
}
// ============================================================================================
// Underlying Staking
// ============================================================================================
function _stakeUnderlying(uint256 _amount) internal virtual;
function _unstakeUnderlying(uint256 _amount) internal virtual;
// ============================================================================================
// Functions: Interest Accumulation and Adjustment
// ============================================================================================
/// @notice The ```AddInterest``` event is emitted when interest is accrued by borrowers
/// @param interestEarned The total interest accrued by all borrowers
/// @param rate The interest rate used to calculate accrued interest
event AddInterest(uint256 interestEarned, uint256 rate);
/// @notice The ```UpdateRate``` event is emitted when the interest rate is updated
/// @param oldRatePerSec The old interest rate (per second)
/// @param oldShares previous used shares
/// @param newRatePerSec The new interest rate (per second)
/// @param newShares new shares
event UpdateRate(
uint256 oldRatePerSec,
uint128 oldShares,
uint256 newRatePerSec,
uint128 newShares
);
/// @notice The ```addInterest``` function is a public implementation of _addInterest and allows 3rd parties to trigger interest accrual
/// @param _returnAccounting Whether to return additional accounting data
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _currentRateInfo The new rate info struct
/// @return _claimableFees The new total of fees that are claimable
/// @return _totalBorrow The new total borrow struct
function addInterest(
bool _returnAccounting
)
external
nonReentrant
returns (
uint256 _interestEarned,
CurrentRateInfo memory _currentRateInfo,
uint256 _claimableFees,
VaultAccount memory _totalBorrow
)
{
(, _interestEarned, _currentRateInfo) = _addInterest();
if (_returnAccounting) {
_claimableFees = claimableFees;
_totalBorrow = totalBorrow;
}
}
/// @notice The ```previewAddInterest``` function
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _newCurrentRateInfo The new rate info struct
/// @return _claimableFees The new total of fees that are claimable
/// @return _totalBorrow The new total borrow struct
function previewAddInterest()
public
view
returns (
uint256 _interestEarned,
CurrentRateInfo memory _newCurrentRateInfo,
uint256 _claimableFees,
VaultAccount memory _totalBorrow
)
{
_newCurrentRateInfo = currentRateInfo;
// Write return values
InterestCalculationResults memory _results = _calculateInterest(_newCurrentRateInfo);
if (_results.isInterestUpdated) {
_interestEarned = _results.interestEarned;
_newCurrentRateInfo.ratePerSec = _results.newRate;
_newCurrentRateInfo.lastShares = _results.newShares;
_claimableFees = claimableFees + uint128(_interestEarned);
_totalBorrow = _results.totalBorrow;
} else {
_claimableFees = claimableFees;
_totalBorrow = totalBorrow;
}
}
struct InterestCalculationResults {
bool isInterestUpdated;
uint64 newRate;
uint128 newShares;
uint256 interestEarned;
VaultAccount totalBorrow;
}
/// @notice The ```_calculateInterest``` function calculates the interest to be accrued and the new interest rate info
/// @param _currentRateInfo The current rate info
/// @return _results The results of the interest calculation
function _calculateInterest(
CurrentRateInfo memory _currentRateInfo
) internal view returns (InterestCalculationResults memory _results) {
// Short circuit if interest already calculated this block
if (_currentRateInfo.lastTimestamp < block.timestamp) {
// Indicate that interest is updated and calculated
_results.isInterestUpdated = true;
// Write return values and use these to save gas
_results.totalBorrow = totalBorrow;
// Time elapsed since last interest update
uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;
// Request new interest rate and full utilization rate from the rate calculator
(_results.newRate, _results.newShares) = IRateCalculator(rateCalculator).getNewRate(
address(collateral),
_deltaTime,
_currentRateInfo.lastShares
);
// Calculate interest accrued
_results.interestEarned = (_deltaTime * _results.totalBorrow.amount * _results.newRate) / RATE_PRECISION;
// Accrue interest (if any) and fees if no overflow
if (
_results.interestEarned > 0 &&
_results.interestEarned + _results.totalBorrow.amount <= type(uint128).max
) {
// Increment totalBorrow by interestEarned
_results.totalBorrow.amount += uint128(_results.interestEarned);
}else{
//reset interest earned
_results.interestEarned = 0;
}
}
}
/// @notice The ```_addInterest``` function is invoked prior to every external function and is used to accrue interest and update interest rate
/// @dev Can only called once per block
/// @return _isInterestUpdated True if interest was calculated
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _currentRateInfo The new rate info struct
function _addInterest()
internal
returns (
bool _isInterestUpdated,
uint256 _interestEarned,
CurrentRateInfo memory _currentRateInfo
)
{
// Pull from storage and set default return values
_currentRateInfo = currentRateInfo;
// Calc interest
InterestCalculationResults memory _results = _calculateInterest(_currentRateInfo);
// Write return values only if interest was updated and calculated
if (_results.isInterestUpdated) {
_isInterestUpdated = _results.isInterestUpdated;
_interestEarned = _results.interestEarned;
// emit here so that we have access to the old values
emit UpdateRate(
_currentRateInfo.ratePerSec,
_currentRateInfo.lastShares,
_results.newRate,
_results.newShares
);
emit AddInterest(_interestEarned, _results.newRate);
// overwrite original values
_currentRateInfo.ratePerSec = _results.newRate;
_currentRateInfo.lastShares = _results.newShares;
_currentRateInfo.lastTimestamp = uint64(block.timestamp);
// Effects: write to state
currentRateInfo = _currentRateInfo;
claimableFees += _interestEarned; //increase claimable fees by interest earned
totalBorrow = _results.totalBorrow;
}
}
// ============================================================================================
// Functions: ExchangeRate
// ============================================================================================
/// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated
/// @param exchangeRate The exchange rate
event UpdateExchangeRate(uint256 exchangeRate);
/// @notice The ```updateExchangeRate``` function is the external implementation of _updateExchangeRate.
/// @dev This function is invoked at most once per block as these queries can be expensive
/// @return _exchangeRate The exchange rate
function updateExchangeRate()
external
nonReentrant
returns (uint256 _exchangeRate)
{
return _updateExchangeRate();
}
/// @notice The ```_updateExchangeRate``` function retrieves the latest exchange rate. i.e how much collateral to buy 1e18 asset.
/// @dev This function is invoked at most once per block as these queries can be expensive
/// @return _exchangeRate The exchange rate
function _updateExchangeRate()
internal
returns (uint256 _exchangeRate)
{
// Pull from storage to save gas and set default return values
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
// Get the latest exchange rate from the oracle
//convert price of collateral as debt is priced in terms of collateral amount (inverse)
_exchangeRate = 1e36 / IOracle(_exchangeRateInfo.oracle).getPrices(address(collateral));
//skip storage writes if value doesnt change
if (_exchangeRate != _exchangeRateInfo.exchangeRate) {
// Effects: Bookkeeping and write to storage
_exchangeRateInfo.lastTimestamp = uint96(block.timestamp);
_exchangeRateInfo.exchangeRate = _exchangeRate;
exchangeRateInfo = _exchangeRateInfo;
emit UpdateExchangeRate(_exchangeRate);
}
}
// ============================================================================================
// Functions: Lending
// ============================================================================================
// ONLY Protocol can lend
// ============================================================================================
// Functions: Borrowing
// ============================================================================================
//sync user collateral by removing account of userCollateralBalance based on
//how many "claimable" redemption tokens are available to the user
//should be called before anything with userCollateralBalance is used
function _syncUserRedemptions(address _account) internal{
//sync rewards first
_checkpoint(_account);
//get token count (divide by LTV_PRECISION as precision is padded)
uint256 rTokens = claimable_reward[address(redemptionWriteOff)][_account] / LTV_PRECISION;
//reset claimables
claimable_reward[address(redemptionWriteOff)][_account] = 0;
//remove from collateral balance the number of rtokens the user has
uint256 currentUserBalance = _userCollateralBalance[_account];
_userCollateralBalance[_account] = currentUserBalance >= rTokens ? currentUserBalance - rTokens : 0;
}
/// @notice The ```Borrow``` event is emitted when a borrower increases their position
/// @param _borrower The borrower whose account was debited
/// @param _receiver The address to which the Asset Tokens were transferred
/// @param _borrowAmount The amount of Asset Tokens transferred
/// @param _sharesAdded The number of Borrow Shares the borrower was debited
/// @param _mintFees The amount of mint fees incurred
event Borrow(
address indexed _borrower,
address indexed _receiver,
uint256 _borrowAmount,
uint256 _sharesAdded,
uint256 _mintFees
);
/// @notice The ```_borrow``` function is the internal implementation for borrowing assets
/// @param _borrowAmount The amount of the Asset Token to borrow
/// @param _receiver The address to receive the Asset Tokens
/// @return _sharesAdded The amount of borrow shares the msg.sender will be debited
function _borrow(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) {
// Get borrow accounting from storage to save gas
VaultAccount memory _totalBorrow = totalBorrow;
if(_borrowAmount < minimumBorrowAmount){
revert InsufficientBorrowAmount();
}
//mint fees
uint256 debtForMint = (_borrowAmount * (LIQ_PRECISION + mintFee) / LIQ_PRECISION);
// Check available capital
uint256 _assetsAvailable = _totalDebtAvailable(_totalBorrow);
if (_assetsAvailable < debtForMint) {
revert InsufficientDebtAvailable(_assetsAvailable, debtForMint);
}
// Calculate the number of shares to add based on the amount to borrow
_sharesAdded = _totalBorrow.toShares(debtForMint, true);
//combine current shares and new shares
uint256 newTotalShares = _totalBorrow.shares + _sharesAdded;
// Effects: Bookkeeping to add shares & amounts to total Borrow accounting
_totalBorrow.amount += debtForMint.toUint128();
_totalBorrow.shares = newTotalShares.toUint128();
// Effects: write back to storage
totalBorrow = _totalBorrow;
_userBorrowShares[msg.sender] += _sharesAdded;
uint256 otherFees = debtForMint > _borrowAmount ? debtForMint - _borrowAmount : 0;
if (otherFees > 0) claimableOtherFees += otherFees;
// Interactions
IResupplyRegistry(registry).mint(_receiver, _borrowAmount);
emit Borrow(msg.sender, _receiver, _borrowAmount, _sharesAdded, otherFees);
}
/// @notice The ```borrow``` function allows a user to open/increase a borrow position
/// @dev Borrower must call ```ERC20.approve``` on the Collateral Token contract if applicable
/// @param _borrowAmount The amount to borrow
/// @param _underlyingAmount The amount of underlying tokens to transfer to Pair
/// @param _receiver The address which will receive the Asset Tokens
/// @return _shares The number of borrow Shares the msg.sender will be debited
function borrow(
uint256 _borrowAmount,
uint256 _underlyingAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) returns (uint256 _shares) {
if (_receiver == address(0)) revert InvalidReceiver();
// Accrue interest if necessary
_addInterest();
// Update _exchangeRate
_updateExchangeRate();
// Only add collateral if necessary
if (_underlyingAmount > 0) {
//pull underlying and deposit in vault
underlying.safeTransferFrom(msg.sender, address(this), _underlyingAmount);
uint256 collateralShares = IERC4626(address(collateral)).deposit(_underlyingAmount, address(this));
//add collateral to msg.sender
_addCollateral(address(this), collateralShares, msg.sender);
}
// Effects: Call internal borrow function
_shares = _borrow(_borrowAmount.toUint128(), _receiver);
}
/// @notice The ```AddCollateral``` event is emitted when a borrower adds collateral to their position
/// @param borrower The borrower account for which the collateral should be credited
/// @param collateralAmount The amount of Collateral Token to be transferred
event AddCollateral(address indexed borrower, uint256 collateralAmount);
/// @notice The ```_addCollateral``` function is an internal implementation for adding collateral to a borrowers position
/// @param _sender The source of funds for the new collateral
/// @param _collateralAmount The amount of Collateral Token to be transferred
/// @param _borrower The borrower account for which the collateral should be credited
function _addCollateral(address _sender, uint256 _collateralAmount, address _borrower) internal {
_userCollateralBalance[_borrower] += _collateralAmount;
if (_sender != address(this)) {
collateral.safeTransferFrom(_sender, address(this), _collateralAmount);
}
//stake underlying
_stakeUnderlying(_collateralAmount);
emit AddCollateral(_borrower, _collateralAmount);
}
/// @notice The ```addCollateral``` function allows the caller to add Collateral Token to a borrowers position
/// @dev msg.sender must call ERC20.approve() on the Collateral Token contract prior to invocation
/// @param _collateralAmount The amount of Collateral Token to be added to borrower's position
/// @param _borrower The account to be credited
function addCollateralVault(uint256 _collateralAmount, address _borrower) external nonReentrant {
if (_borrower == address(0)) revert InvalidReceiver();
_addInterest();
_addCollateral(msg.sender, _collateralAmount, _borrower);
}
/// @notice Allows depositing in terms of underlying asset, and have it converted to collateral shares to the borrower's position.
/// @param _amount The amount of the underlying asset to deposit.
/// @param _borrower The address of the borrower whose collateral balance will be credited.
function addCollateral(uint256 _amount, address _borrower) external nonReentrant {
if (_borrower == address(0)) revert InvalidReceiver();
_addInterest();
underlying.safeTransferFrom(msg.sender, address(this), _amount);
uint256 collateralShares = IERC4626(address(collateral)).deposit(_amount, address(this));
_addCollateral(address(this), collateralShares, _borrower);
}
/// @notice The ```RemoveCollateral``` event is emitted when collateral is removed from a borrower's position
/// @param _collateralAmount The amount of Collateral Token to be transferred
/// @param _receiver The address to which Collateral Tokens will be transferred
/// @param _borrower The address of the account in which collateral is being removed
event RemoveCollateral(
uint256 _collateralAmount,
address indexed _receiver,
address indexed _borrower
);
/// @notice The ```_removeCollateral``` function is the internal implementation for removing collateral from a borrower's position
/// @param _collateralAmount The amount of Collateral Token to remove from the borrower's position
/// @param _receiver The address to receive the Collateral Token transferred
/// @param _borrower The borrower whose account will be debited the Collateral amount
function _removeCollateral(uint256 _collateralAmount, address _receiver, address _borrower) internal {
// Effects: write to state
// NOTE: Following line will revert on underflow if _collateralAmount > userCollateralBalance
_userCollateralBalance[_borrower] -= _collateralAmount;
//unstake underlying
//NOTE: following will revert on underflow if total collateral < _collateralAmount
_unstakeUnderlying(_collateralAmount);
// Interactions
if (_receiver != address(this)) {
collateral.safeTransfer(_receiver, _collateralAmount);
}
emit RemoveCollateral(_collateralAmount, _receiver, _borrower);
}
/// @notice The ```removeCollateralVault``` function is used to remove collateral from msg.sender's borrow position
/// @dev msg.sender must be solvent after invocation or transaction will revert
/// @param _collateralAmount The amount of Collateral Token to transfer
/// @param _receiver The address to receive the transferred funds
function removeCollateralVault(
uint256 _collateralAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) {
//note: isSolvent checkpoints msg.sender via _syncUserRedemptions
if (_receiver == address(0)) revert InvalidReceiver();
_addInterest();
// Note: exchange rate is irrelevant when borrower has no debt shares
if (_userBorrowShares[msg.sender] > 0) {
_updateExchangeRate();
}
_removeCollateral(_collateralAmount, _receiver, msg.sender);
}
/// @notice The ```removeCollateral``` function is used to remove collateral from msg.sender's borrow position and redeem it for underlying tokens
/// @dev msg.sender must be solvent after invocation or transaction will revert
/// @param _collateralAmount The amount of Collateral Token to redeem
/// @param _receiver The address to receive the redeemed underlying tokens
function removeCollateral(
uint256 _collateralAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) {
//note: isSolvent checkpoints msg.sender via _syncUserRedemptions
if (_receiver == address(0)) revert InvalidReceiver();
_addInterest();
// Note: exchange rate is irrelevant when borrower has no debt shares
if (_userBorrowShares[msg.sender] > 0) {
_updateExchangeRate();
}
_removeCollateral(_collateralAmount, address(this), msg.sender);
IERC4626(address(collateral)).redeem(_collateralAmount, _receiver, address(this));
}
/// @notice The ```Repay``` event is emitted whenever a debt position is repaid
/// @param payer The address paying for the repayment
/// @param borrower The borrower whose account will be credited
/// @param amountToRepay The amount of Asset token to be transferred
/// @param shares The amount of Borrow Shares which will be debited from the borrower after repayment
event Repay(address indexed payer, address indexed borrower, uint256 amountToRepay, uint256 shares);
/// @notice The ```_repay``` function is the internal implementation for repaying a borrow position
/// @dev The payer must have called ERC20.approve() on the Asset Token contract prior to invocation
/// @param _totalBorrow An in memory copy of the totalBorrow VaultAccount struct
/// @param _amountToRepay The amount of Asset Token to transfer
/// @param _shares The number of Borrow Shares the sender is repaying
/// @param _payer The address from which funds will be transferred
/// @param _borrower The borrower account which will be credited
function _repay(
VaultAccount memory _totalBorrow,
uint128 _amountToRepay,
uint128 _shares,
address _payer,
address _borrower
) internal {
//checkpoint rewards for borrower before adjusting borrow shares
_checkpoint(_borrower);
// Effects: Bookkeeping
_totalBorrow.amount -= _amountToRepay;
_totalBorrow.shares -= _shares;
// Effects: write user state
uint256 usershares = _userBorrowShares[_borrower] - _shares;
_userBorrowShares[_borrower] = usershares;
//check that any remaining user amount is greater than minimumBorrowAmount
if(usershares > 0 && _totalBorrow.toAmount(usershares, true) < minimumBorrowAmount){
revert InsufficientBorrowAmount();
}
// Effects: write global state
totalBorrow = _totalBorrow;
// Interactions
// burn from non-zero address. zero address is only supplied during liquidations
// for liqudations the handler will do the burning
if (_payer != address(0)) {
IMintable(address(debtToken)).burn(_payer, _amountToRepay);
}
emit Repay(_payer, _borrower, _amountToRepay, _shares);
}
/// @notice The ```repay``` function allows the caller to pay down the debt for a given borrower.
/// @dev Caller must first invoke ```ERC20.approve()``` for the Asset Token contract
/// @param _shares The number of Borrow Shares which will be repaid by the call
/// @param _borrower The account for which the debt will be reduced
/// @return _amountToRepay The amount of Asset Tokens which were burned to repay the Borrow Shares
function repay(uint256 _shares, address _borrower) external nonReentrant returns (uint256 _amountToRepay) {
if (_borrower == address(0)) revert InvalidReceiver();
// Accrue interest if necessary
_addInterest();
// Calculate amount to repay based on shares
VaultAccount memory _totalBorrow = totalBorrow;
_amountToRepay = _totalBorrow.toAmount(_shares, true);
// Execute repayment effects
_repay(_totalBorrow, _amountToRepay.toUint128(), _shares.toUint128(), msg.sender, _borrower);
}
// ============================================================================================
// Functions: Redemptions
// ============================================================================================
event Redeemed(
address indexed _caller,
uint256 _amount,
uint256 _collateralFreed,
uint256 _protocolFee,
uint256 _debtReduction
);
/// @notice Allows redemption of the debt tokens for collateral
/// @dev Only callable by the registry's redeemer contract
/// @param _caller The address of the caller
/// @param _amount The amount of debt tokens to redeem
/// @param _totalFeePct Total fee to charge, expressed as a percentage of the stablecoin input; to be subdivided between protocol and borrowers.
/// @param _receiver The address to receive the collateral tokens
/// @return _collateralToken The address of the collateral token
/// @return _collateralFreed The amount of collateral tokens returned to receiver
function redeemCollateral(
address _caller,
uint256 _amount,
uint256 _totalFeePct,
address _receiver
) external nonReentrant returns(address _collateralToken, uint256 _collateralFreed){
//check sender. must go through the registry's redemptionHandler
if(msg.sender != IResupplyRegistry(registry).redemptionHandler()) revert InvalidRedemptionHandler();
if (_receiver == address(0) || _receiver == address(this)) revert InvalidReceiver();
if(_amount < minimumRedemption){
revert MinimumRedemption();
}
// accrue interest if necessary
_addInterest();
//redemption fees
//assuming 1% redemption fee(0.5% to protocol, 0.5% to borrowers) and a redemption of $100
// reduce totalBorrow.amount by 99.5$
// add 0.5$ to protocol earned fees
// return 99$ of collateral
// burn $100 of stables
uint256 valueToRedeem = _amount * (EXCHANGE_PRECISION - _totalFeePct) / EXCHANGE_PRECISION;
uint256 protocolFee = (_amount - valueToRedeem) * protocolRedemptionFee / EXCHANGE_PRECISION;
uint256 debtReduction = _amount - protocolFee; // protocol fee portion is not burned
//check if theres enough debt to write off
VaultAccount memory _totalBorrow = totalBorrow;
if(debtReduction > _totalBorrow.amount || _totalBorrow.amount - debtReduction < minimumLeftoverDebt ){
revert InsufficientDebtToRedeem(); // size of request exceeeds total pair debt
}
_totalBorrow.amount -= uint128(debtReduction);
//if after many redemptions the amount to shares ratio has deteriorated too far, then refactor
//cast to uint256 to reduce chance of overflow
if(uint256(_totalBorrow.amount) * SHARE_REFACTOR_PRECISION < _totalBorrow.shares){
_increaseRewardEpoch(); //will do final checkpoint on previous total supply
_totalBorrow.shares /= uint128(SHARE_REFACTOR_PRECISION);
}
// Effects: write to state
totalBorrow = _totalBorrow;
claimableOtherFees += protocolFee; //increase claimable fees
// Update exchange rate
uint256 _exchangeRate = _updateExchangeRate();
//calc collateral units
_collateralFreed = ((valueToRedeem * _exchangeRate) / EXCHANGE_PRECISION);
_unstakeUnderlying(_collateralFreed);
_collateralToken = address(collateral);
IERC20(_collateralToken).safeTransfer(_receiver, _collateralFreed);
//distribute write off tokens to adjust userCollateralbalances
//padded with LTV_PRECISION for extra precision
redemptionWriteOff.mint(_collateralFreed * LTV_PRECISION);
emit Redeemed(_caller, _amount, _collateralFreed, protocolFee, debtReduction);
}
// ============================================================================================
// Functions: Liquidations
// ============================================================================================
/// @notice The ```Liquidate``` event is emitted when a liquidation occurs
/// @param _borrower The borrower account for which the liquidation occurred
/// @param _collateralForLiquidator The amount of collateral token transferred to the liquidator
/// @param _sharesLiquidated The number of borrow shares liquidated
/// @param _amountLiquidatorToRepay The amount of asset tokens to be repaid by the liquidator
event Liquidate(
address indexed _borrower,
uint256 _collateralForLiquidator,
uint256 _sharesLiquidated,
uint256 _amountLiquidatorToRepay
);
/// @notice The ```liquidate``` function allows a third party to repay a borrower's debt if they have become insolvent
/// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling ```Liquidate()```
/// @param _borrower The account for which the repayment is credited and from whom collateral will be taken
/// @return _collateralForLiquidator The amount of Collateral Token transferred to the liquidator
function liquidate(
address _borrower
) external nonReentrant returns (uint256 _collateralForLiquidator) {
address liquidationHandler = IResupplyRegistry(registry).liquidationHandler();
if(msg.sender != liquidationHandler) revert InvalidLiquidator();
if (_borrower == address(0)) revert InvalidReceiver();
// accrue interest if necessary
_addInterest();
// Update exchange rate and use the lower rate for liquidations
uint256 _exchangeRate = _updateExchangeRate();
// Check if borrower is solvent, revert if they are
//_isSolventSync calls _syncUserRedemptions which checkpoints rewards and userCollateral
if (_isSolventSync(_borrower, _exchangeRate)) {
revert BorrowerSolvent();
}
// Read from state
VaultAccount memory _totalBorrow = totalBorrow;
uint256 _collateralBalance = _userCollateralBalance[_borrower];
uint128 _borrowerShares = _userBorrowShares[_borrower].toUint128();
// Checks & Calculations
// Determine the liquidation amount in collateral units (i.e. how much debt liquidator is going to repay)
uint256 _liquidationAmountInCollateralUnits = ((_totalBorrow.toAmount(_borrowerShares, false) *
_exchangeRate) / EXCHANGE_PRECISION);
// add fee for liquidation
_collateralForLiquidator = (_liquidationAmountInCollateralUnits *
(LIQ_PRECISION + liquidationFee)) / LIQ_PRECISION;
// clamp to user collateral balance as we cant take more than that
_collateralForLiquidator = _collateralForLiquidator > _collateralBalance ? _collateralBalance : _collateralForLiquidator;
// Calculated here for use during repayment, grouped with other calcs before effects start
uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_borrowerShares, true)).toUint128();
emit Liquidate(
_borrower,
_collateralForLiquidator,
_borrowerShares,
_amountLiquidatorToRepay
);
// Effects & Interactions
// repay using address(0) to skip burning (liquidationHandler will burn from insurance pool)
_repay(
_totalBorrow,
_amountLiquidatorToRepay,
_borrowerShares,
address(0),
_borrower
);
// Collateral is removed on behalf of borrower and sent to liquidationHandler
// NOTE: isSolvent above checkpoints user with _syncUserRedemptions before removing collateral
_removeCollateral(_collateralForLiquidator, liquidationHandler, _borrower);
//call liquidation handler to distribute and burn debt
ILiquidationHandler(liquidationHandler).processLiquidationDebt(address(collateral), _collateralForLiquidator, _amountLiquidatorToRepay);
}
// ============================================================================================
// Functions: Leverage
// ============================================================================================
/// @notice The ```LeveragedPosition``` event is emitted when a borrower takes out a new leveraged position
/// @param _borrower The account for which the debt is debited
/// @param _swapperAddress The address of the swapper which conforms the FraxSwap interface
/// @param _borrowAmount The amount of Asset Token to be borrowed to be borrowed
/// @param _borrowShares The number of Borrow Shares the borrower is credited
/// @param _initialUnderlyingAmount The amount of initial underlying Tokens supplied by the borrower
/// @param _amountCollateralOut The amount of Collateral Token which was received for the Asset Tokens
event LeveragedPosition(
address indexed _borrower,
address _swapperAddress,
uint256 _borrowAmount,
uint256 _borrowShares,
uint256 _initialUnderlyingAmount,
uint256 _amountCollateralOut
);
/// @notice The ```leveragedPosition``` function allows a user to enter a leveraged borrow position with minimal upfront Underlying tokens
/// @dev Caller must invoke ```ERC20.approve()``` on the Underlying Token contract prior to calling function
/// @param _swapperAddress The address of the whitelisted swapper to use to swap borrowed Asset Tokens for Collateral Tokens
/// @param _borrowAmount The amount of Asset Tokens borrowed
/// @param _initialUnderlyingAmount The initial amount of underlying Tokens supplied by the borrower
/// @param _amountCollateralOutMin The minimum amount of Collateral Tokens to be received in exchange for the borrowed Asset Tokens
/// @param _path An array containing the addresses of ERC20 tokens to swap. Adheres to UniV2 style path params.
/// @return _totalCollateralBalance The total amount of Collateral Tokens added to a users account (initial + swap)
function leveragedPosition(
address _swapperAddress,
uint256 _borr...
// [truncated — 57379 bytes total]
SafeERC20.sol 212 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
VaultAccount.sol 39 lines
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.19;
struct VaultAccount {
uint128 amount; // Total amount, analogous to market cap
uint128 shares; // Total shares, analogous to shares outstanding
}
/// @title VaultAccount Library
/// @author Drake Evans (Frax Finance) github.com/drakeevans, modified from work by @Boring_Crypto github.com/boring_crypto
/// @notice Provides a library for use with the VaultAccount struct, provides convenient math implementations
/// @dev Uses uint128 to save on storage
library VaultAccountingLibrary {
/// @notice Calculates the shares value in relationship to `amount` and `total`
/// @dev Given an amount, return the appropriate number of shares
function toShares(VaultAccount memory total, uint256 amount, bool roundUp) internal pure returns (uint256 shares) {
if (total.amount == 0) {
shares = amount;
} else {
shares = (amount * total.shares) / total.amount;
if (roundUp && (shares * total.amount) / total.shares < amount) {
shares = shares + 1;
}
}
}
/// @notice Calculates the amount value in relationship to `shares` and `total`
/// @dev Given a number of shares, returns the appropriate amount
function toAmount(VaultAccount memory total, uint256 shares, bool roundUp) internal pure returns (uint256 amount) {
if (total.shares == 0) {
amount = shares;
} else {
amount = (shares * total.amount) / total.shares;
if (roundUp && total.amount > 0 && (amount * total.shares) / total.amount < shares) {
amount = amount + 1;
}
}
}
}
IRateCalculator.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IRateCalculator {
function name() external view returns (string memory);
function version() external view returns (uint256, uint256, uint256);
function getNewRate(
address _vault,
uint256 _deltaTime,
uint256 _previousShares
) external view returns (uint64 _newRatePerSec, uint128 _newShares);
}
ISwapper.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface ISwapper {
function swap(
address account,
uint256 amountIn,
address[] calldata path,
address to
) external;
function swapPools(address tokenIn, address tokenOut) external view returns(address swappool, int32 tokenInIndex, int32 tokenOutIndex, uint32 swaptype);
}
IFeeDeposit.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IFeeDeposit {
function operator() external view returns(address);
function lastDistributedEpoch() external view returns(uint256);
function setOperator(address _newAddress) external;
function distributeFees() external;
function incrementPairRevenue(uint256 _fees, uint256 _otherFees) external;
function getEpoch() external view returns(uint256);
}
IResupplyRegistry.sol 73 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IResupplyRegistry {
event AddPair(address pairAddress);
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SetDeployer(address deployer, bool _bool);
event DefaultSwappersSet(address[] addresses);
event EntryUpdated(string indexed key, address indexed addr);
event WithdrawTo(address indexed user, uint256 amount);
// Protected keys
function LIQUIDATION_HANDLER() external pure returns (string memory);
function FEE_DEPOSIT() external pure returns (string memory);
function REDEMPTION_HANDLER() external pure returns (string memory);
function INSURANCE_POOL() external pure returns (string memory);
function REWARD_HANDLER() external pure returns (string memory);
function TREASURY() external pure returns (string memory);
function STAKER() external pure returns (string memory);
function L2_MANAGER() external pure returns (string memory);
function VEST_MANAGER() external pure returns (string memory);
// Other public functions
function token() external view returns (address);
function govToken() external view returns (address);
function getAddress(string memory key) external view returns (address);
function getAllKeys() external view returns (string[] memory);
function getAllAddresses() external view returns (address[] memory);
function getProtectedKeys() external pure returns (string[] memory);
function keyExists(string memory) external view returns (bool);
function hashToKey(bytes32) external view returns (string memory);
function setAddress(string memory key, address addr) external;
function acceptOwnership() external;
function addPair(address _pairAddress) external;
function registeredPairs(uint256) external view returns (address);
function pairsByName(string memory) external view returns (address);
function registeredPairsLength() external view returns (uint256);
function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray);
function defaultSwappers(uint256 _index) external view returns (address);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function renounceOwnership() external;
function transferOwnership(address newOwner) external;
function claimFees(address _pair) external;
function claimRewards(address _pair) external;
function claimInsuranceRewards() external;
function withdrawTo(address _asset, uint256 _amount, address _to) external;
function mint(address receiver, uint256 amount) external;
function burn(address target, uint256 amount) external;
function liquidationHandler() external view returns(address);
function feeDeposit() external view returns(address);
function redemptionHandler() external view returns(address);
function rewardHandler() external view returns(address);
function insurancePool() external view returns(address);
function setRewardClaimer(address _newAddress) external;
function setRedemptionHandler(address _newAddress) external;
function setFeeDeposit(address _newAddress) external;
function setLiquidationHandler(address _newAddress) external;
function setInsurancePool(address _newAddress) external;
function setStaker(address _newAddress) external;
function setTreasury(address _newAddress) external;
function staker() external view returns(address);
function treasury() external view returns(address);
function l2manager() external view returns(address);
function setRewardHandler(address _newAddress) external;
function setVestManager(address _newAddress) external;
function setDefaultSwappers(address[] memory _swappers) external;
function collateralId(address _collateral) external view returns(uint256);
error NameMustBeUnique();
error ProtectedKey(string key);
}
IConvexStaking.sol 25 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IConvexStaking {
function poolInfo(uint256 _pid) external view returns(
address lptoken,
address token,
address gauge,
address crvRewards,
address stash,
bool shutdown
);
function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns(bool);
function depositAll(uint256 _pid, bool _stake) external returns(bool);
function withdrawAndUnwrap(uint256 amount, bool claim) external returns(bool);
function withdrawAllAndUnwrap(bool claim) external;
function getReward() external returns(bool);
function getReward(address _account, bool _claimExtras) external returns(bool);
function totalSupply() external view returns (uint256);
function extraRewardsLength() external view returns (uint256);
function extraRewards(uint256 _rid) external view returns (address _rewardContract);
function rewardToken() external view returns (address _rewardToken);
function token() external view returns (address _token);
function balanceOf(address account) external view returns (uint256);
}
EpochTracker.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "../interfaces/ICore.sol";
/**
@title EpochTracker
@dev Provides a unified `startTime` and `getEpoch`, used for tracking epochs.
*/
contract EpochTracker {
uint256 public immutable startTime;
/// @notice Length of an epoch, in seconds
uint256 public immutable epochLength;
constructor(address _core) {
startTime = ICore(_core).startTime();
epochLength = ICore(_core).epochLength();
}
function getEpoch() public view returns (uint256 epoch) {
return (block.timestamp - startTime) / epochLength;
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
IOracle.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IOracle {
function decimals() external view returns (uint8);
function getPrices(address _vault) external view returns (uint256 _price);
function name() external view returns (string memory);
}
ILiquidationHandler.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface ILiquidationHandler {
function operator() external view returns(address);
function setOperator(address _newAddress) external;
function processLiquidationDebt(address _collateral, uint256 _collateralAmount, uint256 _debtAmount) external;
function processCollateral(address _collateral) external;
function liquidate(
address _pair,
address _borrower
) external returns (uint256 _collateralForLiquidator);
}
RewardDistributorMultiEpoch.sol 321 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
//abstract reward handling to attach to another contract
//supports an epoch system for supply changes
abstract contract RewardDistributorMultiEpoch is ReentrancyGuard{
using SafeERC20 for IERC20;
struct EarnedData {
address token;
uint256 amount;
}
struct RewardType {
address reward_token;
bool is_non_claimable; //a bit unothrodox setting but need to block claims on our redemption tokens as they will be processed differently
uint256 reward_remaining;
}
//rewards
RewardType[] public rewards;
uint256 public currentRewardEpoch;
mapping(address => uint256) public userRewardEpoch; //account -> epoch
mapping(uint256 => mapping(address => uint256)) public global_reward_integral; //epoch -> token -> integral
mapping(uint256 => mapping(address => mapping(address => uint256))) public reward_integral_for;// epoch -> token -> account -> integral
mapping(address => mapping(address => uint256)) public claimable_reward;//token -> account -> claimable
mapping(address => uint256) public rewardMap;
mapping(address => address) public rewardRedirect;
uint256 constant private PRECISION = 1e22;
//events
event RewardPaid(address indexed _user, address indexed _rewardToken, address indexed _receiver, uint256 _rewardAmount);
event RewardAdded(address indexed _rewardToken);
event RewardInvalidated(address indexed _rewardToken);
event RewardRedirected(address indexed _account, address _forward);
event NewEpoch(uint256 indexed _epoch);
constructor() {
}
modifier onlyRewardManager() {
require(_isRewardManager(), "!rewardManager");
_;
}
/////////
// Abstract functions
////////
function _isRewardManager() internal view virtual returns(bool);
function _fetchIncentives() internal virtual;
function _totalRewardShares() internal view virtual returns(uint256);
function _userRewardShares(address _account) internal view virtual returns(uint256);
function _increaseUserRewardEpoch(address _account, uint256 _currentUserEpoch) internal virtual;
function _checkAddToken(address _address) internal view virtual returns(bool);
//////////
function maxRewards() public pure virtual returns(uint256){
return 15;
}
//register an extra reward token to be handled
function addExtraReward(address _token) external onlyRewardManager nonReentrant{
//add to reward list
_insertRewardToken(_token);
}
//insert a new reward, ignore if already registered or invalid
function _insertRewardToken(address _token) internal{
if(_token == address(this) || _token == address(0) || !_checkAddToken(_token)){
//dont allow reward tracking of the staking token or invalid address
return;
}
//add to reward list if new
if(rewardMap[_token] == 0){
//check reward count for new additions
require(rewards.length < maxRewards(), "max rewards");
//set token
RewardType storage r = rewards.push();
r.reward_token = _token;
//set map index after push (mapped value is +1 of real index)
rewardMap[_token] = rewards.length;
emit RewardAdded(_token);
//workaround: transfer 0 to self so that earned() reports correctly
//with new tokens
if(_token.code.length > 0){
IERC20(_token).safeTransfer(address(this), 0);
}else{
//non contract address added? invalidate
_invalidateReward(_token);
}
}else{
//get previous used index of given token
//this ensures that reviving can only be done on the previous used slot
uint256 index = rewardMap[_token];
//index is rewardMap minus one
RewardType storage reward = rewards[index-1];
//check if it was invalidated
if(reward.reward_token == address(0)){
//revive
reward.reward_token = _token;
emit RewardAdded(_token);
}
}
}
//allow invalidating a reward if the token causes trouble in calcRewardIntegral
function invalidateReward(address _token) external onlyRewardManager nonReentrant{
_invalidateReward(_token);
}
function _invalidateReward(address _token) internal{
uint256 index = rewardMap[_token];
if(index > 0){
//index is registered rewards minus one
RewardType storage reward = rewards[index-1];
require(reward.reward_token == _token, "!mismatch");
//set reward token address to 0, integral calc will now skip
reward.reward_token = address(0);
emit RewardInvalidated(_token);
}
}
//get reward count
function rewardLength() external view returns(uint256) {
return rewards.length;
}
//calculate and record an account's earnings of the given reward. if _claimTo is given it will also claim.
function _calcRewardIntegral(uint256 _epoch, uint256 _currentEpoch, uint256 _index, address _account, address _claimTo) internal{
RewardType storage reward = rewards[_index];
address rewardToken = reward.reward_token;
//skip invalidated rewards
//if a reward token starts throwing an error, calcRewardIntegral needs a way to exit
if(rewardToken == address(0)){
return;
}
//get difference in balance and remaining rewards
//getReward is unguarded so we use reward_remaining to keep track of how much was actually claimed since last checkpoint
uint256 bal = IERC20(rewardToken).balanceOf(address(this));
uint256 remainingRewards = reward.reward_remaining;
//update the global integral but only for the current epoch
if (_epoch == _currentEpoch && _totalRewardShares() > 0 && bal > remainingRewards) {
uint256 rewardPerToken = ((bal - remainingRewards) * PRECISION / _totalRewardShares());
if(rewardPerToken > 0){
//increase integral
global_reward_integral[_epoch][rewardToken] += rewardPerToken;
}else{
//set balance as current reward_remaining to let dust grow
bal = remainingRewards;
}
}
uint256 reward_global = global_reward_integral[_epoch][rewardToken];
if(_account != address(0)){
//update user integrals
uint userI = reward_integral_for[_epoch][rewardToken][_account];
if(_claimTo != address(0) || userI < reward_global){
//_claimTo address non-zero means its a claim
// only allow claims if current epoch and if the reward allows it
if(_epoch == _currentEpoch && _claimTo != address(0) && !reward.is_non_claimable){
uint256 receiveable = claimable_reward[rewardToken][_account] + (_userRewardShares(_account) * (reward_global - userI) / PRECISION);
if(receiveable > 0){
claimable_reward[rewardToken][_account] = 0;
IERC20(rewardToken).safeTransfer(_claimTo, receiveable);
emit RewardPaid(_account, rewardToken, _claimTo, receiveable);
//remove what was claimed from balance
bal -= receiveable;
}
}else{
claimable_reward[rewardToken][_account] = claimable_reward[rewardToken][_account] + ( _userRewardShares(_account) * (reward_global - userI) / PRECISION);
}
reward_integral_for[_epoch][rewardToken][_account] = reward_global;
}
}
//update remaining reward so that next claim can properly calculate the balance change
//claims and tracking new rewards should only happen on current epoch
if(_epoch == _currentEpoch && bal != remainingRewards){
reward.reward_remaining = bal;
}
}
function _increaseRewardEpoch() internal{
//final checkpoint for this epoch
_checkpoint(address(0), address(0), type(uint256).max);
//move epoch up
uint256 newEpoch = currentRewardEpoch + 1;
currentRewardEpoch = newEpoch;
emit NewEpoch(newEpoch);
}
//checkpoint without claiming
function _checkpoint(address _account) internal {
//checkpoint without claiming by passing address(0)
//default to max as most operations such as deposit/withdraw etc needs to fully sync beforehand
_checkpoint(_account, address(0), type(uint256).max);
}
//checkpoint with claim
function _checkpoint(address _account, address _claimTo, uint256 _maxloops) internal {
//claim rewards first
_fetchIncentives();
uint256 globalEpoch = currentRewardEpoch;
uint256 rewardCount = rewards.length;
for (uint256 loops = 0; loops < _maxloops;) {
uint256 userEpoch = globalEpoch;
if(_account != address(0)){
//take user epoch
userEpoch = userRewardEpoch[_account];
//if no shares then jump to current epoch
if(userEpoch != globalEpoch && _userRewardShares(_account) == 0){
userEpoch = globalEpoch;
userRewardEpoch[_account] = userEpoch;
}
}
//calc reward integrals
for(uint256 i = 0; i < rewardCount;){
_calcRewardIntegral(userEpoch, globalEpoch, i,_account,_claimTo);
unchecked { i += 1; }
}
if(userEpoch < globalEpoch){
_increaseUserRewardEpoch(_account, userEpoch);
}else{
return;
}
unchecked { loops += 1; }
}
}
//manually checkpoint a user account
function user_checkpoint(address _account, uint256 _epochloops) external nonReentrant returns(bool) {
_checkpoint(_account, address(0), _epochloops);
return true;
}
//get earned token info
//change ABI to view to use this off chain
function earned(address _account) public nonReentrant virtual returns(EarnedData[] memory claimable) {
//because this is a state mutative function
//we can simplify the earned() logic of all rewards (internal and external)
//and allow this contract to be agnostic to outside reward contract design
//by just claiming everything and updating state via _checkpoint()
_checkpoint(_account);
uint256 rewardCount = rewards.length;
claimable = new EarnedData[](rewardCount);
for (uint256 i = 0; i < rewardCount;) {
RewardType storage reward = rewards[i];
//skip invalidated and non claimable rewards
if(reward.reward_token == address(0) || reward.is_non_claimable){
unchecked{ i += 1; }
continue;
}
claimable[i].amount = claimable_reward[reward.reward_token][_account];
claimable[i].token = reward.reward_token;
unchecked{ i += 1; }
}
return claimable;
}
//set any claimed rewards to automatically go to a different address
//set address to zero to disable
function setRewardRedirect(address _to) external nonReentrant{
rewardRedirect[msg.sender] = _to;
emit RewardRedirected(msg.sender, _to);
}
//claim reward for given account (unguarded)
function getReward(address _account) public virtual nonReentrant {
//check if there is a redirect address
address redirect = rewardRedirect[_account];
if(redirect != address(0)){
_checkpoint(_account, redirect, type(uint256).max);
}else{
//claim directly in checkpoint logic to save a bit of gas
_checkpoint(_account, _account, type(uint256).max);
}
}
//claim reward for given account and forward (guarded)
function getReward(address _account, address _forwardTo) public virtual nonReentrant{
//in order to forward, must be called by the account itself
require(msg.sender == _account, "!self");
require(_forwardTo != address(0), "fwd address cannot be 0");
//use _forwardTo address instead of _account
_checkpoint(_account, _forwardTo, type(uint256).max);
}
}
WriteOffToken.sol 50 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/*
A token-like contract to track write offs via erc20 interfaces to be used
in reward distribution logic
interfaces needed:
- balanceOf
- transfer
- mint
*/
contract WriteOffToken {
address public immutable owner;
uint256 public totalSupply;
constructor(address _owner)
{
owner = _owner;
}
function name() external pure returns (string memory){
return "WriteOffToken";
}
function symbol() external pure returns (string memory){
return "WOT";
}
function decimals() external pure returns (uint8){
return 18;
}
function mint(uint256 _amount) external{
if(msg.sender == owner){
totalSupply += _amount;
}
}
function transfer(address to, uint256 amount) external returns (bool){
return true;
}
function balanceOf(address) external view returns (uint256){
//just return total supply
return totalSupply;
}
}
IERC4626.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed caller,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
function asset() external view returns (address);
function convertToAssets(uint256 shares) external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function maxDeposit(address) external view returns (uint256);
function maxMint(address) external view returns (uint256);
function maxRedeem(address owner) external view returns (uint256);
function maxWithdraw(address owner) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);
function previewMint(uint256 shares) external view returns (uint256);
function previewRedeem(uint256 shares) external view returns (uint256);
function previewWithdraw(uint256 assets) external view returns (uint256);
function totalAssets() external view returns (uint256);
function mint(uint256 shares, address receiver) external returns (uint256 assets);
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
}
CoreOwnable.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ICore} from "../interfaces/ICore.sol";
/**
@title Core Ownable
@author Prisma Finance (with edits by Resupply Finance)
@notice Contracts inheriting `CoreOwnable` have the same owner as `Core`.
The ownership cannot be independently modified or renounced.
*/
contract CoreOwnable {
ICore public immutable core;
constructor(address _core) {
core = ICore(_core);
}
modifier onlyOwner() {
require(msg.sender == address(core), "!core");
_;
}
function owner() public view returns (address) {
return address(core);
}
}
IMintable.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IMintable{
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
}
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
ICore.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { IAuthHook } from './IAuthHook.sol';
interface ICore {
struct OperatorAuth {
bool authorized;
IAuthHook hook;
}
event VoterSet(address indexed newVoter);
event OperatorExecuted(address indexed caller, address indexed target, bytes data);
event OperatorSet(address indexed caller, address indexed target, bool authorized, bytes4 selector, IAuthHook authHook);
function execute(address target, bytes calldata data) external returns (bytes memory);
function epochLength() external view returns (uint256);
function startTime() external view returns (uint256);
function voter() external view returns (address);
function ownershipTransferDeadline() external view returns (uint256);
function pendingOwner() external view returns (address);
function setOperatorPermissions(
address caller,
address target,
bytes4 selector,
bool authorized,
IAuthHook authHook
) external;
function setVoter(address newVoter) external;
function operatorPermissions(address caller, address target, bytes4 selector) external view returns (bool authorized, IAuthHook hook);
}
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
IAuthHook.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IAuthHook {
function preHook(address operator, address target, bytes calldata data) external returns (bool);
function postHook(bytes memory result, address operator, address target, bytes calldata data) external returns (bool);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Read Contract
CRV 0x945c9142 → address
CVX 0x759cb53b → address
EXCHANGE_PRECISION 0xc0a7e892 → uint256
LIQ_PRECISION 0x9fe34bdc → uint256
LTV_PRECISION 0xb78294dd → uint256
PAIR_DECIMALS 0xc49bb2d1 → uint256
RATE_PRECISION 0x2b3ba681 → uint256
SHARE_REFACTOR_PRECISION 0x146a29eb → uint256
borrowLimit 0xe551d11d → uint256
claimableFees 0xc3192f14 → uint256
claimableOtherFees 0x2164e85b → uint256
claimable_reward 0x33fd6f74 → uint256
collateral 0xd8dfeb45 → address
convexBooster 0x2cdacb50 → address
convexPid 0x86993bef → uint256
core 0xf2f4eb26 → address
currentRateInfo 0x95d14ca8 → uint64, uint64, uint128
currentRewardEpoch 0x56ecf28b → uint256
currentUtilization 0xfff5d9da → uint256
epochLength 0x57d775f8 → uint256
exchangeRateInfo 0xfbbbf94c → address, uint96, uint256
getConstants 0x9a295e73 → uint256, uint256, uint256, uint256
getEpoch 0x757991a8 → uint256
getPairAccounting 0xcdd72d52 → uint256, uint128, uint128, uint256
global_reward_integral 0xe69bc271 → uint256
lastFeeEpoch 0xca51bb59 → uint256
liquidationFee 0xa36a3630 → uint256
maxLTV 0xf384bd05 → uint256
maxRewards 0xd619658b → uint256
minimumBorrowAmount 0x08c7e366 → uint256
minimumLeftoverDebt 0x92bbcaed → uint256
minimumRedemption 0x8f50ea92 → uint256
mintFee 0x13966db5 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
previewAddInterest 0xcacf3b58 → uint256, tuple, uint256, tuple
protocolRedemptionFee 0xab7cfaf9 → uint256
rateCalculator 0x0df8dfac → address
redemptionWriteOff 0x42f43d0b → address
registry 0x7b103999 → address
rewardLength 0xb95c5746 → uint256
rewardMap 0x83d44339 → uint256
rewardRedirect 0xe509b9d9 → address
reward_integral_for 0x91ebebbd → uint256
rewards 0xf301af42 → address, bool, uint256
startTime 0x78e97925 → uint256
swappers 0x8cad7fbe → bool
toBorrowAmount 0x7ec4b571 → uint256
toBorrowShares 0x93f46f64 → uint256
totalBorrow 0x8285ef40 → uint128, uint128
totalCollateral 0x4ac8eb5f → uint256
totalDebtAvailable 0x93ae0df9 → uint256
underlying 0x6f307dc3 → address
userBorrowShares 0x4fd422df → uint256
userRewardEpoch 0xb5b54547 → uint256
version 0x54fd4d50 → uint256, uint256, uint256
Write Contract 36 functions
These functions modify contract state and require a wallet transaction to execute.
addCollateral 0xcadac479
uint256 _amount
address _borrower
addCollateralVault 0x60c52d05
uint256 _collateralAmount
address _borrower
addExtraReward 0x5e43c47b
address _token
addInterest 0x1c6c9597
bool _returnAccounting
returns: uint256, tuple, uint256, tuple
borrow 0xd6bda0c0
uint256 _borrowAmount
uint256 _underlyingAmount
address _receiver
returns: uint256
earned 0x008cc262
address _account
returns: tuple[]
getReward 0x6b091695
address _account
address _forwardTo
getReward 0xc00007b0
address _account
getUserSnapshot 0xb68d0a09
address _address
returns: uint256, uint256
invalidateReward 0xf8112eed
address _token
leveragedPosition 0xa053db68
address _swapperAddress
uint256 _borrowAmount
uint256 _initialUnderlyingAmount
uint256 _amountCollateralOutMin
address[] _path
returns: uint256
liquidate 0x2f865568
address _borrower
returns: uint256
pause 0x8456cb59
No parameters
redeemCollateral 0xfd6d0526
address _caller
uint256 _amount
uint256 _totalFeePct
address _receiver
returns: address, uint256
removeCollateral 0xd41ddc96
uint256 _collateralAmount
address _receiver
removeCollateralVault 0x8049d971
uint256 _collateralAmount
address _receiver
repay 0xacb70815
uint256 _shares
address _borrower
returns: uint256
repayWithCollateral 0x0cab937d
address _swapperAddress
uint256 _collateralToSwap
uint256 _amountOutMin
address[] _path
returns: uint256
setBorrowLimit 0xe7a33174
uint256 _limit
setConvexPool 0x7e929684
uint256 pid
setLiquidationFees 0x730a7514
uint256 _newLiquidationFee
setMaxLTV 0x08a0c375
uint256 _newMaxLTV
setMinimumBorrowAmount 0x4cae6513
uint256 _min
setMinimumLeftoverDebt 0x8214ba48
uint256 _min
setMinimumRedemption 0xd870ce66
uint256 _min
setMintFees 0x06b6f7e9
uint256 _newMintFee
setOracle 0x7adbf973
address _newOracle
setProtocolRedemptionFee 0x6551f16c
uint256 _fee
setRateCalculator 0x48741376
address _newRateCalculator
bool _updateInterest
setRewardRedirect 0x75a41014
address _to
setSwapper 0x3f2617cb
address _swapper
bool _approval
unpause 0x3f4ba83a
No parameters
updateExchangeRate 0x02ce728f
No parameters
returns: uint256
userCollateralBalance 0xb5af3062
address _account
returns: uint256
user_checkpoint 0xf5c7f899
address _account
uint256 _epochloops
returns: bool
withdrawFees 0x476343ee
No parameters
returns: uint256, uint256
Recent Transactions
No transactions found for this address