Address Contract Partially Verified
Address
0x00000000efe883b3304aFf71eaCf72Dbc3e1b577
Balance
0 ETH
Nonce
1
Code Size
10609 bytes
Creator
0xc0136F2b...50C9 at tx 0x423e2e5b...a46bd4
Indexed Transactions
0
Contract Bytecode
10609 bytes
0x60806040526004361015610011575f80fd5b5f5f3560e01c80628cc2621461137557806301e1d1141461135b57806306fdde031461130457806307a2d13a146112e6578063085ce8d5146110b25780630a28a47714611093578063146a29eb1461107357806318160ddd146110555780631fc3277d14611037578063313ce5671461101b57806333fd6f7414610fc857806338d52e0f14610f83578063402d267d1461064657806345cb3dde14610f655780634cdad50614610f465780634ea63d4b14610e855780634f04a86b14610e6757806356ecf28b14610e495780635e43c47b14610e145780635e7a7bdf14610dcf5780636b09169514610cf45780636e553f6514610bf857806370a08231146105df5780637298da7e14610bdd57806375a4101414610b5d5780637b10399914610b1857806383d4433914610adf5780638da5cb5b1461038f57806390ab8d1f14610aa657806391ebebbd14610a5957806394bf804d1461092f57806395d89b41146108eb578063b3d7f6b9146108cc578063b460af94146107e9578063b5b54547146107b0578063b95c574614610792578063ba08765214610670578063c00007b01461064b578063c63d75b614610646578063c6e6f592146103d4578063ce96cb771461061f578063d619658b14610603578063d905777e146105df578063e18c755b14610526578063e509b9d9146104e5578063e69bc271146104a0578063e9fad8ee14610419578063ec1a947b146103fb578063ef8b30f7146103d4578063f2f4eb261461038f578063f301af4214610339578063f5c7f899146102fc578063f8112eed146102bb5763fdf364e41461026b575f80fd5b346102b857806003193601126102b857610283611aa4565b61028c33611df1565b60018155337f35240c11d1c44c1b33caab278e6b9711ac32ce37b14bfb66b360c90f0e227a2b8280a280f35b80fd5b50346102b85760203660031901126102b8576102f56102d8611545565b6102e86102e36119f3565b6117ae565b6102f0611aa4565b612489565b6001815580f35b50346102b85760403660031901126102b85760019061032d61031c611545565b610324611aa4565b60243590612341565b55602060405160018152f35b50346102b85760203660031901126102b857600435906001548210156102b8576060610364836115fa565b506001815491015460ff6040519260018060a01b038116845260a01c16151560208301526040820152f35b50346102b857806003193601126102b8576040517f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03168152602090f35b50346102b85760203660031901126102b85760206103f360043561195e565b604051908152f35b50346102b857806003193601126102b8576020600b54604051908152f35b50346102b857806003193601126102b857610432611aa4565b61043b33611df1565b60018155610448336118f8565b610454600c5442611985565b338252600e60205280604083205561046b3361186a565b9060405191825260208201527fa345023a4eda421d4dbe108023f197e12ac173da60a22f9c5a82765eff7f4bf960403392a280f35b50346102b85760403660031901126102b85760406104bc61155b565b9160043581526004602052209060018060a01b03165f52602052602060405f2054604051908152f35b50346102b85760203660031901126102b8576020906001600160a01b0361050a611545565b16815260088252604060018060a01b0391205416604051908152f35b50346102b85760203660031901126102b85760043561056f337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03161461177a565b670de0b6b3a764000081106105b0576020817f850e8bd4ae6b3e61561ab5f3f5286a6c8db7abde1c18d8052ccfdce7966df05792600b55604051908152a180f35b60405162461bcd60e51b8152602060048201526007602482015266746f6f206c6f7760c81b6044820152606490fd5b50346102b85760203660031901126102b85760206103f36105fe611545565b61186a565b50346102b857806003193601126102b85760206040515f198152f35b50346102b85760203660031901126102b85760206103f36106416105fe611545565b611732565b61159b565b50346102b85760203660031901126102b85761066d610668611545565b6118f8565b80f35b50346102b85761067f366115c0565b9291610689611aa4565b91928392906001600160a01b0316330361078e576106a633611d3e565b806106bb575b60208460018555604051908152f35b9092506106c733611df1565b6106d081611732565b91821561075b57602093826106e760019433611eac565b6107198583868060a01b037f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec16612571565b60405190815284868201523391848060a01b0316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a491926106ac565b60405162461bcd60e51b815260206004820152600b60248201526a5a45524f5f41535345545360a81b6044820152606490fd5b8280fd5b50346102b857806003193601126102b8576020600154604051908152f35b50346102b85760203660031901126102b8576020906040906001600160a01b036107d8611545565b168152600383522054604051908152f35b50346102b8576107f8366115c0565b90610801611aa4565b83916001600160a01b031633036108c8579260209361081f33611d3e565b83610833575b848360018455604051908152f35b6001925061084033611df1565b610849846119aa565b936108548533611eac565b6108868183868060a01b037f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec16612571565b60405190858252868201523391848060a01b0316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a48291610825565b8380fd5b50346102b85760203660031901126102b85760206103f3600435611d13565b50346102b857806003193601126102b8575061092b60405161090e604082611646565b60048152630726549560e41b602082015260405191829182611571565b0390f35b50346102b85760403660031901126102b85760209060043561094f61155b565b91610958611aa4565b6001600160a01b038316808252600e855260408220549192839261097c901561182c565b61098585611fb4565b80610999575b858460018555604051908152f35b600193506109a681611d13565b94856109b6575b5084935061098b565b816109c091611c70565b610a206040516323b872dd60e01b88820152336024820152306044820152866064820152606481526109f3608482611646565b858060a01b037f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec1661290c565b60405190858252868201527fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d760403392a35f80806109ad565b50346102b857604090610a6b366115c0565b9391839193526005602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b50346102b85760203660031901126102b8576020906040906001600160a01b03610ace611545565b168152600e83522054604051908152f35b50346102b85760203660031901126102b8576020906040906001600160a01b03610b07611545565b168152600783522054604051908152f35b50346102b857806003193601126102b8576040517f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03168152602090f35b50346102b85760203660031901126102b857610b77611545565b610b7f611aa4565b3380835260086020908152604080852080546001600160a01b0319166001600160a01b039590951694851790555192835290917ff4239ad0860f93469699dd4be8040b8838c5e25bb6cf24a1dfb381b937ff078c9190a26001815580f35b50346102b857806003193601126102b85760206103f36118d5565b50346102b85760403660031901126102b857602090600435610c1861155b565b91610c21611aa4565b6001600160a01b038316808252600e8552604082205491928392610c45901561182c565b610c4e85611fb4565b80610c6157858460018555604051908152f35b60019350610c6e8161195e565b948580610c7e575b94505061098b565b610c8791611c70565b610cba6040516323b872dd60e01b88820152336024820152306044820152826064820152606481526109f3608482611646565b60405190815284868201527fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d760403392a35f808085610c76565b50346102b85760403660031901126102b857610d0e611545565b610d1661155b565b6001600160a01b038216808452600e6020526040842054610d3790156117eb565b610d3f611aa4565b3303610da2576001600160a01b03811615610d5d576102f5916121cc565b60405162461bcd60e51b815260206004820152601760248201527f66776420616464726573732063616e6e6f7420626520300000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606490fd5b50346102b857806003193601126102b8576040517f0000000000000000000000008b36ad6a6605745529908c90ccc90f05901155b46001600160a01b03168152602090f35b50346102b85760203660031901126102b8576102f5610e31611545565b610e3c6102e36119f3565b610e44611aa4565b611ac2565b50346102b857806003193601126102b8576020600254604051908152f35b50346102b857806003193601126102b8576020600d54604051908152f35b50346102b85760403660031901126102b857600435602435610ed1337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03161461177a565b621275008211610f1657816040917ff65507692ceb91f4fd4ae78cec2f6a0a1571e81421c737b20d574343d8b1068793600c5580600d5582519182526020820152a180f35b60405162461bcd60e51b81526020600482015260086024820152670e8dede40d0d2ced60c31b6044820152606490fd5b50346102b85760203660031901126102b85760206103f3600435611732565b50346102b857806003193601126102b8576020600c54604051908152f35b50346102b857806003193601126102b8576040517f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03168152602090f35b50346102b85760403660031901126102b8576040610fe4611545565b91610fed61155b565b9260018060a01b031681526006602052209060018060a01b03165f52602052602060405f2054604051908152f35b50346102b857806003193601126102b857602060405160128152f35b50346102b857806003193601126102b8576020604051621275008152f35b50346102b857806003193601126102b8576020600a54604051908152f35b50346102b857806003193601126102b857602060405164e8d4a510008152f35b50346102b85760203660031901126102b85760206103f36004356119aa565b50346112495760203660031901126112495760405163d25adeb360e01b81526004803591906020908290817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa90811561123e575f916112b7575b506001600160a01b031633036112835761112f6118d5565b811161124d577f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b0316803b1561124957604051632770a7eb60e21b815230600482015260248101929092525f908290604490829084905af1801561123e5761122b575b50600a546111a5611668565b64e8d4a5100081029080820464e8d4a5100014901517156112175781116111ca575080f35b6111d2612129565b6002546001810180911161121757908164e8d4a51000926002557febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3358480a204600a5580f35b634e487b7160e01b83526011600452602483fd5b61123791505f90611646565b5f5f611199565b6040513d5f823e3d90fd5b5f80fd5b60405162461bcd60e51b815260206004820152600e60248201526d216d696e696d756d41737365747360901b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b10b634b8903430b7323632b960a11b6044820152606490fd5b6112d9915060203d6020116112df575b6112d18183611646565b81019061175b565b5f611117565b503d6112c7565b346112495760203660031901126112495760206103f3600435611732565b34611249575f3660031901126112495761092b604051611325604082611646565b601781527f5265737570706c7920496e737572616e636520506f6f6c000000000000000000602082015260405191829182611571565b34611249575f3660031901126112495760206103f3611668565b346112495760203660031901126112495761138e611545565b611396611aa4565b61139f81611fb4565b600154906113ac82611992565b916113ba6040519384611646565b8083526113c681611992565b602084019290601f19015f5b8181106114fb5750506001600160a01b0316905f5b81811061148457505060015f555f52600e60205260405f205461145d575b90604051918291602083019060208452518091526040830191905f5b81811061142f575050500390f35b825180516001600160a01b031685526020908101518186015286955060409094019390920191600101611421565b815115611470575f602082510152611405565b634e487b7160e01b5f52603260045260245ffd5b806114906001926115fa565b5054828060a01b0381169081159081156114ed575b506114e557805f52600660205260405f20855f5260205260405f205460206114cd848a611632565b5101526114da8288611632565b515201905b906113e7565b5001906114df565b60ff915060a01c16886114a5565b60405190604082019180831067ffffffffffffffff841117611531576020926040525f81525f83820152828289010152016113d2565b634e487b7160e01b5f52604160045260245ffd5b600435906001600160a01b038216820361124957565b602435906001600160a01b038216820361124957565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b34611249576020366003190112611249576115b4611545565b5060206040515f198152f35b606090600319011261124957600435906024356001600160a01b038116810361124957906044356001600160a01b03811681036112495790565b6001548110156114705760015f81815291901b7fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60191565b80518210156114705760209160051b010190565b90601f8019910116810190811067ffffffffffffffff82111761153157604052565b6040516370a0823160e01b81523060048201526020816024817f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03165afa90811561123e575f916116be575090565b90506020813d6020116116e5575b816116d960209383611646565b81010312611249575190565b3d91506116cc565b8181029291811591840414171561170057565b634e487b7160e01b5f52601160045260245ffd5b811561171e570490565b634e487b7160e01b5f52601260045260245ffd5b600a5480156117575761174f6117549261174a611668565b6116ed565b611714565b90565b5090565b9081602091031261124957516001600160a01b03811681036112495790565b1561178157565b60405162461bcd60e51b815260206004820152600560248201526421636f726560d81b6044820152606490fd5b156117b557565b60405162461bcd60e51b815260206004820152600e60248201526d10b932bbb0b93226b0b730b3b2b960911b6044820152606490fd5b156117f257565b60405162461bcd60e51b815260206004820152601260248201527118db185a5b481dda1a5b19481c5d595d595960721b6044820152606490fd5b1561183357565b60405162461bcd60e51b815260206004820152600f60248201526e1dda5d1a191c985dc81c5d595d5959608a1b6044820152606490fd5b6001600160a01b03165f9081526009602090815260408083205460025460039093529220549192919080821061189e575050565b909264e8d4a510006001915b049301928184146118c35764e8d4a510006001916118aa565b925050565b9190820391821161170057565b600b546118e0611668565b90808211156118f257611754916118c8565b50505f90565b6001600160a01b0381165f818152600e602052604090205461191a90156117eb565b611922611aa4565b5f908152600860205260409020546001600160a01b0316801561194e57611948916121cc565b60015f55565b5080611959916121cc565b611948565b600a548061196a575090565b61197790611754926116ed565b61197f611668565b90611714565b9190820180921161170057565b67ffffffffffffffff81116115315760051b60200190565b600a54806119b75750905b565b91906119dd6119c4611668565b9361174f6119d68661174f84876116ed565b95866116ed565b106119e457565b90600181018091116117005790565b337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b0316148015611a285790565b5060405163670fb82160e01b81526020816004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa90811561123e575f91611a85575b506001600160a01b0316331490565b611a9e915060203d6020116112df576112d18183611646565b5f611a76565b60025f5414611ab35760025f55565b633ee5aeb560e01b5f5260045ffd5b6001600160a01b038116903082148015611c68575b8015611c58575b611c1e57815f52600760205260405f2054155f14611bea576001545f19811015611bb7576801000000000000000081101561153157806001611b2392016001556115fa565b50826bffffffffffffffffffffffff60a01b825416179055600154825f52600760205260405f2055817fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2803b15611bad57506119b5906040519063a9059cbb60e01b60208301523060248301525f604483015260448252611ba8606483611646565b61290c565b6119b59150612489565b60405162461bcd60e51b815260206004820152600b60248201526a6d6178207265776172647360a81b6044820152606490fd5b50805f52600760205260405f20545f19810190811161170057611c0c906115fa565b5080546001600160a01b031615611c22575b5050565b80546001600160a01b031916821790557fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2565b50611c6281612535565b15611ade565b508115611ad7565b6001600160a01b0316908115611cce577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602082611cb15f94600a54611985565b600a558484526009825260408420818154019055604051908152a3565b60405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606490fd5b600a5491908215611d3a576119dd611d29611668565b61174f6119d68661174f86856116ed565b9150565b6001600160a01b03165f908152600e602052604090205480151580611de7575b15611db157600d54611d6f91611985565b4211611d7757565b60405162461bcd60e51b81526020600482015260126024820152713bb4ba34323930bb903a34b6b29037bb32b960711b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d2177697468647261772074696d6560901b6044820152606490fd5b5080421015611d5e565b6001600160a01b0381165f818152600e6020526040902054909190611e14575050565b611e1d90611fb4565b60015415611470577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6546001600160a01b03165f908152600660209081526040808320848452909152812080549190557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf780549091611e9b916118c8565b90555f52600e6020525f6040812055565b6001600160a01b03168015611f6557805f52600960205260405f205491808310611f15576020817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef925f958587526009845203604086205580600a5403600a55604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b5f907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15611249575f80916004604051809481936325f2da9160e21b83525af1801561123e57612114575b506002549060015490835b5f198110612024575050505050565b836001600160a01b038316806120cb575b865b8581106120a457508582101561209b57808752600960205264e8d4a51000604088205404818852600960205260408820556001820180921161208757865260036020526040862055600101612015565b634e487b7160e01b87526011600452602487fd5b50505050505050565b806120bc89876001948b889c999d9e9a979b986125ad565b01919490939796929591612037565b8087526003602052604087205491508582141580612100575b1561203557905084908087526003602052856040882055612035565b5080875260096020526040872054156120e4565b6121219192505f90611646565b5f905f61200a565b5f7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15611249575f80916004604051809481936325f2da9160e21b83525af1801561123e576121b9575b5060025460015491905f19821061219457505050565b815b8381106121a35750505050565b806121b3848060019486806125ad565b01612196565b6121c591505f90611646565b5f5f61217e565b7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b031691823b15611249576040516325f2da9160e21b81525f816004818380985af1801561123e5761232c575b506002549160015492845b5f19811061223b57505050505050565b816001600160a01b038416806122e3575b875b8781106122bc5750838210156122b257808852600960205264e8d4a51000604089205404818952600960205260408920556001820180921161229e5787526003602052604087205560010161222b565b634e487b7160e01b88526011600452602488fd5b5050505050505050565b806122d4888860019489889d999e9f9b979c986125ad565b0192969195909498979261224e565b8088526003602052604088205491508382141580612318575b1561224c5790508290808852600360205283604089205561224c565b5080885260096020526040882054156122fc565b6123399193505f90611646565b5f915f612220565b5f91907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15611249575f80916004604051809481936325f2da9160e21b83525af1801561123e57612474575b506002549060015491845b8481106123b257505050505050565b816001600160a01b0384168061242b575b875b8681106124155750838210156122b257808852600960205264e8d4a51000604089205404818952600960205260408920556001820180921161229e578752600360205260408720556001016123a3565b806124258a8860019489886125ad565b016123c5565b8088526003602052604088205491508382141580612460575b156123c3579050829080885260036020528360408920556123c3565b508088526009602052604088205415612444565b6124819193505f90611646565b5f915f612398565b6001600160a01b03165f81815260076020526040902054806124a9575050565b5f198101908111611700576124bd906115fa565b5080546001600160a01b03168290036125045780546001600160a01b03191690557f646cfe9445aed85f4853d501d1924d2bdabb1bbf12531df29f929f07ba4169e05f80a2565b60405162461bcd60e51b8152602060048201526009602482015268042dad2e6dac2e8c6d60bb1b6044820152606490fd5b7f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b0390811691161461256d57600190565b5f90565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526119b591611ba8606483611646565b9392916125b9906115fa565b5080546001600160a01b031692908315612904576040516370a0823160e01b815230600482015293602085602481845afa94851561123e575f956128d0575b5084966001830196875495821496868880996128c5575b806128bc575b61283f575b50505f8281526004602090815260408083206001600160a01b0387811685529252909120549516938461266b575b50505050505081612660575b5061265d575050565b55565b90508214155f612654565b825f52600560205260405f2060018060a01b0385165f5260205260405f20855f5260205260405f20548860018060a01b03841692831580158091612836575b6126b8575b50505050612648565b8261282e575b508161281e575b50156127be5761271990855f52600660205260405f20875f5260205269021e19e0c9bab240000061271260405f205492895f52600960205261270c60405f2054918c6118c8565b906116ed565b0490611985565b918261275b575b5050505b5f52600560205260405f209060018060a01b03165f5260205260405f20905f5260205260405f20555f8080808080808881806126af565b90612784836127b5959c93875f52600660205260405f20895f526020525f604081205587612571565b84867fce405e67b4d6e56e438257e15f160ae28b450e6e7659bbc4c1f4e09a1ac846cb6020604051878152a46118c8565b965f8080612720565b90506128029150835f52600660205260405f20855f5260205269021e19e0c9bab240000061271260405f205492875f52600960205261270c60405f2054918a6118c8565b825f52600660205260405f20845f5260205260405f2055612724565b60ff91505460a01c16155f6126c5565b91505f6126be565b508984106126aa565b612848916118c8565b69021e19e0c9bab240000081029080820469021e19e0c9bab2400000149015171561170057600a5461287991611714565b80156128b257825f52600460205260405f2060018060a01b0385165f526020526128a860405f20918254611985565b90555b5f8661261a565b50975084976128ab565b50808211612615565b50600a54151561260f565b9094506020813d6020116128fc575b816128ec60209383611646565b810103126112495751935f6125f8565b3d91506128df565b505050505050565b905f602091828151910182855af11561123e575f513d61295b57506001600160a01b0381163b155b61293b5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561293456fea164736f6c634300081c000a
Verified Source Code Partial Match
Compiler: v0.8.28+commit.7893614a
EVM: cancun
Optimization: Yes (200 runs)
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);
}
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";
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";
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);
}
ERC20.sol 312 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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}.
*
* All two of these 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);
}
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);
}
SafeERC20.sol 199 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.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 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);
}
}
Address.sol 150 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
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;
}
}
Errors.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}
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;
}
}
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);
}
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);
}
}
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);
}
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);
}
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;
}
IResupplyRegistry.sol 60 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);
function acceptOwnership() external;
function addPair(address _pairAddress) external;
function registeredPairs(uint256) external view returns (address);
function pairsByName(string memory) external view returns (address);
function defaultSwappersLength() external view returns (uint256);
function registeredPairsLength() external view returns (uint256);
function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray);
function getAllDefaultSwappers() external view returns (address[] memory _defaultSwappers);
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 token() external view returns(address);
function treasury() external view returns(address);
function govToken() 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);
}
SafeERC20.sol 67 lines
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.19;
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SafeERC20 as OZSafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// solhint-disable avoid-low-level-calls
// solhint-disable max-line-length
/// @title SafeERC20 provides helper functions for safe transfers as well as safe metadata access
/// @author Library originally written by @Boring_Crypto github.com/boring_crypto, modified by Drake Evans (Frax Finance) github.com/drakeevans
/// @dev original: https://github.com/boringcrypto/BoringSolidity/blob/fed25c5d43cb7ce20764cd0b838e21a02ea162e9/contracts/libraries/BoringERC20.sol
library SafeERC20 {
bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
bytes4 private constant SIG_NAME = 0x06fdde03; // name()
bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
function returnDataToString(bytes memory data) internal pure returns (string memory) {
if (data.length >= 64) {
return abi.decode(data, (string));
} else if (data.length == 32) {
uint8 i = 0;
while (i < 32 && data[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && data[i] != 0; i++) {
bytesArray[i] = data[i];
}
return string(bytesArray);
} else {
return "???";
}
}
/// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token symbol.
function safeSymbol(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token name.
function safeName(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
/// @param token The address of the ERC-20 token contract.
/// @return (uint8) Token decimals.
function safeDecimals(IERC20 token) internal view returns (uint8) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
function safeTransfer(IERC20 token, address to, uint256 value) internal {
OZSafeERC20.safeTransfer(token, to, value);
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
OZSafeERC20.safeTransferFrom(token, from, to, value);
}
}
InsurancePool.sol 448 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "../libraries/SafeERC20.sol";
import { IMintable } from "../interfaces/IMintable.sol";
import { RewardDistributorMultiEpoch } from "./RewardDistributorMultiEpoch.sol";
import { IResupplyRegistry } from "../interfaces/IResupplyRegistry.sol";
import { CoreOwnable } from '../dependencies/CoreOwnable.sol';
contract InsurancePool is RewardDistributorMultiEpoch, CoreOwnable{
using SafeERC20 for IERC20;
address immutable public asset;
address immutable public registry;
mapping(address => uint256) private _balances;
uint256 private _totalSupply;
uint256 constant public SHARE_REFACTOR_PRECISION = 1e12;
uint256 public minimumHeldAssets = 10_000 * 1e18;
uint256 public withdrawTime = 7 days;
uint256 public withdrawTimeLimit = 1 days;
mapping(address => uint256) public withdrawQueue;
address public immutable emissionsReceiver;
uint256 public constant MAX_WITHDRAW_DELAY = 14 days;
//events
event Deposit(
address indexed sender,
address indexed owner,
uint256 assets,
uint256 shares
);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 shares,
uint256 assets
);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Cooldown(address indexed account, uint256 amount, uint256 end);
event ExitCancel(address indexed account);
event WithdrawTimersUpdated(uint256 withdrawTime, uint256 withdrawWindow);
event MinimumHeldAssetsUpdated(uint256 minimumAssets);
constructor(address _core, address _registry, address _asset, address[] memory _rewards, address _emissionsReceiver) CoreOwnable(_core){
asset = _asset;
registry = _registry;
emissionsReceiver = _emissionsReceiver;
//initialize rewards list with passed in reward tokens
//NOTE: slot 0 should be emission based extra reward
for(uint256 i = 0; i < _rewards.length;){
_insertRewardToken(_rewards[i]);
unchecked { i += 1; }
}
//mint unbacked shares to this address
//deployment should send the outstanding amount
_mint(address(this), 1e18);
}
function name() external pure returns (string memory){
return "Resupply Insurance Pool";
}
function symbol() external pure returns (string memory){
return "reIP";
}
function decimals() external pure returns (uint8){
return 18;
}
/// @notice set unlock length and withdraw window
/// @param _withdrawLength time to unlock
/// @param _withdrawWindow time to withdraw after unlock
function setWithdrawTimers(uint256 _withdrawLength, uint256 _withdrawWindow) external onlyOwner{
require(_withdrawLength <= MAX_WITHDRAW_DELAY, "too high");
withdrawTime = _withdrawLength;
withdrawTimeLimit = _withdrawWindow;
emit WithdrawTimersUpdated(_withdrawLength, _withdrawWindow);
}
/// @notice set a minimum amount of assets that must be kept from being burned
/// @param _minimum the amount of assets to protect from burn
function setMinimumHeldAssets(uint256 _minimum) external onlyOwner{
require(_minimum >= 1e18, "too low");
minimumHeldAssets = _minimum;
emit MinimumHeldAssetsUpdated(_minimum);
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/// @notice balance of a given account
/// @param _account the user account
function balanceOf(address _account) public view returns (uint256 userBalance) {
userBalance = _balances[_account];
uint256 globalEpoch = currentRewardEpoch;
uint256 userEpoch = userRewardEpoch[_account];
if(userEpoch < globalEpoch){
//need to calculate balance while keeping this as a view function
for(;;){
//reduce shares by refactoring amount
userBalance /= SHARE_REFACTOR_PRECISION;
unchecked {
userEpoch += 1;
}
if(userEpoch == globalEpoch){
break;
}
}
}
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
// ============================================================================================
// 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).claimInsuranceRewards();
}
function _totalRewardShares() internal view override returns(uint256){
return _totalSupply;
}
function _userRewardShares(address _account) internal view override returns(uint256){
return _balances[_account];
}
function _increaseUserRewardEpoch(address _account, uint256 _currentUserEpoch) internal override{
//convert shares to next epoch shares
//share refactoring will never be 0
_balances[_account] = _balances[_account] / SHARE_REFACTOR_PRECISION;
//update user reward epoch
userRewardEpoch[_account] = _currentUserEpoch + 1;
}
function _checkAddToken(address _address) internal view override returns(bool){
if(_address == asset) return false;
return true;
}
//we cant limit reward types since collaterals could be sent as rewards
//however reward lists growing too large is undesirable
//governance should act if too many are added
function maxRewards() public pure override returns(uint256){
return type(uint256).max;
}
function maxBurnableAssets() public view returns(uint256){
uint256 minimumHeld = minimumHeldAssets;
uint256 _totalAssets = totalAssets();
return _totalAssets > minimumHeld ? _totalAssets - minimumHeld : 0;
}
/// @notice burn underlying, liquidationHandler will send rewards in exchange
/// @param _amount the amount to burn
function burnAssets(uint256 _amount) external {
require(msg.sender == IResupplyRegistry(registry).liquidationHandler(), "!liq handler");
require(_amount <= maxBurnableAssets(), "!minimumAssets");
IMintable(asset).burn(address(this), _amount);
//if after many burns the amount to shares ratio has deteriorated too far, then refactor
uint256 tsupply = _totalSupply;
if(totalAssets() * SHARE_REFACTOR_PRECISION < tsupply){
_increaseRewardEpoch(); //will do final checkpoint on previous total supply
tsupply /= SHARE_REFACTOR_PRECISION;
_totalSupply = tsupply;
}
}
/// @notice deposit assets into the insurance pool
/// @param _assets the amount of tokens to deposit
/// @param _receiver the receiving address
/// @return shares amount of shares minted
function deposit(uint256 _assets, address _receiver) external nonReentrant returns (uint256 shares){
//can not deposit if in withdraw queue, call cancel first
require(withdrawQueue[_receiver] == 0,"withdraw queued");
//checkpoint rewards before balance change
_checkpoint(_receiver);
if (_assets > 0) {
shares = previewDeposit(_assets);
if(shares > 0){
_mint(_receiver, shares);
IERC20(asset).safeTransferFrom(msg.sender, address(this), _assets);
emit Deposit(msg.sender, _receiver, _assets, shares);
}
}
}
/// @notice mint shares
/// @param _shares the amount of shares to mint
/// @param _receiver the receving address
/// @return assets amount of assets minted
function mint(uint256 _shares, address _receiver) external nonReentrant returns (uint256 assets){
//can not deposit if in withdraw queue, call cancel first
require(withdrawQueue[_receiver] == 0,"withdraw queued");
//checkpoint rewards before balance change
_checkpoint(_receiver);
if (_shares > 0) {
assets = previewMint(_shares);
if(assets > 0){
_mint(_receiver, _shares);
IERC20(asset).safeTransferFrom(msg.sender, address(this), assets);
emit Deposit(msg.sender, _receiver, assets, _shares);
}
}
}
/// @notice start unlock timing for msg.sender
function exit() external{
//clear any previous withdraw queue and restart
_clearWithdrawQueueGuarded(msg.sender);
//claim all rewards now because reward0 will be excluded during
//the withdraw sequence
//will error if already in withdraw process
getReward(msg.sender);
//set withdraw time
uint256 exitTime = block.timestamp + withdrawTime;
withdrawQueue[msg.sender] = exitTime;
emit Cooldown(msg.sender, balanceOf(msg.sender), exitTime);
}
/// @notice cancel exit timer for msg.sender
function cancelExit() external{
//canceling will remove claimable emissions
//but will redistribute those claimable back into the pool
//thus a portion will go back to msg.sender in accordance with its weight
_clearWithdrawQueueGuarded(msg.sender);
emit ExitCancel(msg.sender);
}
function _clearWithdrawQueueGuarded(address _account) internal nonReentrant{
_clearWithdrawQueue(_account);
}
function _clearWithdrawQueue(address _account) internal {
if(withdrawQueue[_account] != 0){
//checkpoint rewards
_checkpoint(_account);
//get reward 0 info
RewardType storage reward = rewards[0];
address rewardToken = reward.reward_token;
//note how much is claimable
uint256 reward0 = claimable_reward[rewardToken][_account];
//reset claimable
claimable_reward[rewardToken][_account] = 0;
//redistribute back to pool
reward.reward_remaining -= reward0;
withdrawQueue[_account] = 0; //flag as not waiting for withdraw
}
}
function _checkWithdrawReady(address _account) internal view{
uint256 exitTime = withdrawQueue[_account];
require(exitTime > 0 && block.timestamp >= exitTime, "!withdraw time");
require(block.timestamp <= exitTime + withdrawTimeLimit, "withdraw time over");
}
/// @notice burn shares and withdraw underlying assets
/// @param _shares number of shares to redeem
/// @param _receiver address to send underlying to
/// @param _owner the account to redeem from (must equal msg.sender)
/// @return assets amount of asset tokens received
function redeem(uint256 _shares, address _receiver, address _owner) external nonReentrant returns (uint256 assets){
require(msg.sender == _owner);
_checkWithdrawReady(msg.sender);
if (_shares > 0) {
//clear queue will also checkpoint rewards
_clearWithdrawQueue(msg.sender);
assets = previewRedeem(_shares);
require(assets != 0, "ZERO_ASSETS");
_burn(msg.sender, _shares);
IERC20(asset).safeTransfer(_receiver, assets);
emit Withdraw(msg.sender, _receiver, msg.sender, _shares, assets);
}
}
/// @notice withdraw underlying assets
/// @param _amount amount of underlying assets to withdraw
/// @param _receiver the receiving address
/// @param _owner the account to redeem from (must equal msg.sender)
/// @return shares amount of shares burned
function withdraw(uint256 _amount, address _receiver, address _owner) external nonReentrant returns(uint256 shares){
require(msg.sender == _owner);
_checkWithdrawReady(msg.sender);
if (_amount > 0) {
//clear queue will also checkpoint rewards
_clearWithdrawQueue(msg.sender);
shares = previewWithdraw(_amount);
_burn(msg.sender, shares);
IERC20(asset).safeTransfer(_receiver, _amount);
emit Withdraw(msg.sender, _receiver, msg.sender, shares, _amount);
}
}
/// @notice get rewards for the given account
/// @param _account the account to claim rewards for
function getReward(address _account) public override{
require(withdrawQueue[_account] == 0, "claim while queued");
super.getReward(_account);
}
/// @notice get rewards for the given account
/// @param _account the account to claim rewards for
/// @param _forwardTo the address to send claimed rewards to
function getReward(address _account, address _forwardTo) public override{
require(withdrawQueue[_account] == 0, "claim while queued");
super.getReward(_account,_forwardTo);
}
/// @notice check what rewards are claimable
/// @param _account the account to query
/// @dev not a view function
function earned(address _account) public override returns(EarnedData[] memory claimable) {
claimable = super.earned(_account);
if(withdrawQueue[_account] > 0){
claimable[0].amount = 0;
}
}
function totalAssets() public view returns(uint256 assets){
assets = IERC20(asset).balanceOf(address(this));
}
function convertToShares(uint256 _assets) public view returns (uint256 shares){
uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
shares = _assets;
} else {
shares = _assets * _totalSupply / totalAssets();
}
}
function convertToAssets(uint256 _shares) public view returns (uint256 assets){
uint256 _totalSupply = totalSupply();
if(_totalSupply > 0){
assets = totalAssets() * _shares / _totalSupply;
} else{
assets = _shares;
}
}
function convertToSharesRoundUp(uint256 _assets) internal view returns (uint256 shares){
uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
shares = _assets;
} else {
uint256 _totalAssets = totalAssets();
shares = _assets * _totalSupply / _totalAssets;
if ( shares * _totalAssets / _totalSupply < _assets) {
shares = shares + 1;
}
}
}
function convertToAssetsRoundUp(uint256 _shares) internal view returns (uint256 assets){
uint256 _totalSupply = totalSupply();
if(_totalSupply > 0){
uint256 _totalAssets = totalAssets();
assets = _totalAssets * _shares / _totalSupply;
if ( assets * _totalSupply / _totalAssets < _shares) {
assets = assets + 1;
}
}else{
assets = _shares;
}
}
function maxDeposit(address /*_receiver*/) external pure returns (uint256){
return type(uint256).max;
}
function maxMint(address /*_receiver*/) external pure returns (uint256){
return type(uint256).max;
}
function previewDeposit(uint256 _amount) public view returns (uint256){
return convertToShares(_amount);
}
function previewMint(uint256 _shares) public view returns (uint256){
return convertToAssetsRoundUp(_shares); //round up
}
function maxWithdraw(address _owner) external view returns (uint256){
return convertToAssets(balanceOf(_owner));
}
function previewWithdraw(uint256 _amount) public view returns (uint256){
return convertToSharesRoundUp(_amount); //round up
}
function maxRedeem(address _owner) external view returns (uint256){
return balanceOf(_owner);
}
function previewRedeem(uint256 _shares) public view returns (uint256){
return convertToAssets(_shares);
}
}
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);
}
}
Read Contract
MAX_WITHDRAW_DELAY 0x1fc3277d → uint256
SHARE_REFACTOR_PRECISION 0x146a29eb → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
claimable_reward 0x33fd6f74 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
core 0xf2f4eb26 → address
currentRewardEpoch 0x56ecf28b → uint256
decimals 0x313ce567 → uint8
emissionsReceiver 0x5e7a7bdf → address
global_reward_integral 0xe69bc271 → uint256
maxBurnableAssets 0x7298da7e → uint256
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxRewards 0xd619658b → uint256
maxWithdraw 0xce96cb77 → uint256
minimumHeldAssets 0xec1a947b → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
registry 0x7b103999 → address
rewardLength 0xb95c5746 → uint256
rewardMap 0x83d44339 → uint256
rewardRedirect 0xe509b9d9 → address
reward_integral_for 0x91ebebbd → uint256
rewards 0xf301af42 → address, bool, uint256
symbol 0x95d89b41 → string
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256
userRewardEpoch 0xb5b54547 → uint256
withdrawQueue 0x90ab8d1f → uint256
withdrawTime 0x45cb3dde → uint256
withdrawTimeLimit 0x4f04a86b → uint256
Write Contract 16 functions
These functions modify contract state and require a wallet transaction to execute.
addExtraReward 0x5e43c47b
address _token
burnAssets 0x085ce8d5
uint256 _amount
cancelExit 0xfdf364e4
No parameters
deposit 0x6e553f65
uint256 _assets
address _receiver
returns: uint256
earned 0x008cc262
address _account
returns: tuple[]
exit 0xe9fad8ee
No parameters
getReward 0x6b091695
address _account
address _forwardTo
getReward 0xc00007b0
address _account
invalidateReward 0xf8112eed
address _token
mint 0x94bf804d
uint256 _shares
address _receiver
returns: uint256
redeem 0xba087652
uint256 _shares
address _receiver
address _owner
returns: uint256
setMinimumHeldAssets 0xe18c755b
uint256 _minimum
setRewardRedirect 0x75a41014
address _to
setWithdrawTimers 0x4ea63d4b
uint256 _withdrawLength
uint256 _withdrawWindow
user_checkpoint 0xf5c7f899
address _account
uint256 _epochloops
returns: bool
withdraw 0xb460af94
uint256 _amount
address _receiver
address _owner
returns: uint256
Recent Transactions
No transactions found for this address