Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x00000000efe883b3304aFf71eaCf72Dbc3e1b577
Balance 0 ETH
Nonce 1
Code Size 10609 bytes
Indexed Transactions 0
External Etherscan · Sourcify

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