Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xaa376916255f1B4347e40f2f819f4F1382fC6988
Balance 0 ETH
Nonce 1
Code Size 13202 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

13202 bytes
0x6080604052600436101561004c575b361561001957600080fd5b604080513381523460208201527f29a047f6fd84d8cae8b3b33dbfe103e58528cfd4af40aab48f111b019ed380a39190a1005b6000803560e01c806301e1d11414611a7f57806306fdde03146119db57806307a2d13a1461141557806308ba9b6514611975578063095ea7b3146118c45780630a28a477146118a55780630fe28908146116a3578063157a7f201461168857806318160ddd1461165d5780631ac460ca146116345780631d87e7c31461160b57806323b872dd146115d25780632be11ae2146115b75780632c76d7a61461158e5780632cdacb5014611565578063313ce56714611516578063349123d11461149257806335dfce9c1461147757806338d52e0f146114415780633e413bee1461141a578063402d267d14610cd25780634cdad506146114155780635e7b5fbf14611372578063637af4df1461134f578063646780df146113265780636a4874a1146112fd5780636e553f65146112c957806370a082311461047e578063715018a61461125f57806379054cc9146112365780637b67ce451461121b5780638520a343146112005780638da5cb5b146111ca578063923c1d61146111a157806394bf804d1461116d57806395d89b41146110945780639b19251a14611055578063a9059cbb14611023578063b050ecb814610ffa578063b3d7f6b914610fdb578063b460af9414610f45578063b48b688814610f2a578063b54a31a614610f07578063b90817f214610d63578063ba08765214610cd7578063c63d75b614610cd2578063c6e6f59214610318578063cb83d93414610514578063ce96cb77146104cd578063d905777e1461047e578063dd62ed3e14610435578063e529ee9514610417578063e53b20171461031d578063ef8b30f714610318578063f2fde38b146102eb5763fa64458d146102c0575061000e565b346102e857806003193601126102e8576009546040516001600160a01b039091168152602090f35b80fd5b50346102e85760203660031901126102e857610315610308611b06565b610310611c64565b611c9d565b80f35b611c46565b50346102e85760203660031901126102e857610337611b06565b61033f611c64565b610347612350565b9060018060a01b03838160065416803b15610413578190604460405180948193636197390160e11b83528960048401528160248401525af18015610408576103c8575b50916103be816020937f8dddfb0fe32b5cc02812108dd5fe699a4ce5aaaf1db07b1612566cef381a26b795600354166125b0565b604051908152a180f35b836020937f8dddfb0fe32b5cc02812108dd5fe699a4ce5aaaf1db07b1612566cef381a26b79593966103fc6103be94611b58565b9693509350935061038a565b6040513d87823e3d90fd5b5080fd5b50346102e857806003193601126102e8576020600b54604051908152f35b50346102e85760403660031901126102e8576040602091610454611b06565b61046561045f611b1c565b91611bd8565b6001600160a01b03909116825283522054604051908152f35b50346102e85760203660031901126102e85760206104c561049d611b06565b6001600160a01b0316600090815260008051602061327d833981519152602052604090205490565b604051908152f35b50346102e85760203660031901126102e8576020906104c5906040906001600160a01b036104f9611b06565b16815260008051602061327d83398151915284522054611fdd565b50346102e8576101a090816003193601126102e8576040519182018281106001600160401b03821117610cbe576040526004356001600160a01b0381168103610413578252610561611b1c565b60208301526044356001600160a01b03811681036104135760408301526064356001600160a01b03811681036104135760608301526084356001600160a01b038116810361041357608083015260a4356001600160a01b03811681036104135760a083015260c4356001600160a01b03811681036104135760c083015260e4356001600160a01b03811681036104135760e0830152610104356101008301526101243562ffffff8116810361041357610120830152610144356001600160a01b038116810361041357610140830152610164356001600160a01b038116810361041357610160830152610184356001600160a01b03811690036102e8576101843561018083015260008051602061333d833981519152546001600160401b0381161580610cb0575b60016001600160401b038316149081610ca6575b159081610c9d575b50610c8b5760016001600160401b031982161760008051602061333d8339815191525560ff8160401c1615610c5e575b6106dd6120eb565b6106e56120eb565b6106ee33611c9d565b82516001600160a01b03166107016120eb565b6107096120eb565b6107128161214a565b9015610c5557905b6000805160206132fd83398151915280546001600160a81b03191660a09390931b60ff60a01b16929092171790556040519261075584611b81565b602084527f52657475726e2046696e616e636520436f6e7665782055534443205661756c7460208501526040519361078c85611b81565b600985526872664376785553444360b81b60208601526107aa6120eb565b6107b26120eb565b8051906001600160401b038211610c415781906107dd60008051602061325d83398151915254611d11565b601f8111610bc2575b50602090601f8311600114610b32578692610b27575b50508160011b916000199060031b1c19161760008051602061325d833981519152555b83516001600160401b038111610b135761084760008051602061329d83398151915254611d11565b601f8111610a9f575b506020601f8211600114610a0f578190859660ff9692610a04575b50508160011b916000199060031b1c19161760008051602061329d833981519152555b805184546001600160a01b03199081166001600160a01b0392831617865560208301516001805483169184169190911790556040808401516002805484169185169190911790556060840151600380548416918516919091179055608084015160048054841691851691909117905560a084015160058054841691851691909117905560c084015160068054841691851691909117905560e0840151600780548416918516919091179055610100840151600b55610120840151600c805462ffffff191662ffffff9290921691909117905561014084015160088054841691851691909117905561016084015160098054841691851691909117905561018090930151600a805490921692169190911790551c16156109aa5780f35b68ff00000000000000001960008051602061333d833981519152541660008051602061333d833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b01519050388061086b565b60008051602061329d833981519152855260008051602061331d83398151915295855b601f1984168110610a87575095829160ff9697600194601f19811610610a6e575b505050811b0160008051602061329d8339815191525561088e565b015160001960f88460031b161c19169055388080610a53565b82820151885560019097019660209283019201610a32565b60008051602061329d8339815191528552601f820160051c60008051602061331d8339815191520160208310610afe575b601f820160051c60008051602061331d833981519152018110610af35750610850565b858155600101610ad0565b5060008051602061331d833981519152610ad0565b634e487b7160e01b84526041600452602484fd5b0151905038806107fc565b925060008051602061325d833981519152865260008051602061323d8339815191529086935b601f1984168510610ba7576001945083601f19811610610b8e575b505050811b0160008051602061325d8339815191525561081f565b015160001960f88460031b161c19169055388080610b73565b81810151835560209485019460019093019290910190610b58565b90915060008051602061325d8339815191528652601f830160051c60008051602061323d8339815191520160208410610c2c575b908392915b601f820160051c60008051602061323d833981519152018110610c1e57506107e6565b878155849350600101610bfb565b5060008051602061323d833981519152610bf6565b634e487b7160e01b85526041600452602485fd5b5060129061071a565b68ffffffffffffffffff198116680100000000000000011760008051602061333d833981519152556106d5565b60405163f92ee8a960e01b8152600490fd5b905015386106a5565b303b15915061069d565b5060ff8160401c1615610689565b634e487b7160e01b82526041600452602482fd5b611b32565b50346102e8576040610ce836611c11565b6001600160a01b038116855260008051602061327d8339815191526020529290932054909290808411610d2f5750826104c591610d26602095611fdd565b93849133612f27565b604051632e52afbb60e21b81526001600160a01b039390931660048401526024830193909352506044810191909152606490fd5b50346102e85760208060031936011261041357610d7e611b06565b610d86611c64565b600654604051631e8c5c8960e11b81526001600160a01b039391829082906004908290899089165af1801561040857610eda575b50826002541691604051936370a0823160e01b938486523060048701528386602481845afa958615610ea0578796610eab575b508382600154169560246040518098819382523060048301525afa948515610ea0578795610e63575b508483879593610e4e7ffd38deee9dd9b40181927b0f4bd9a3139fe37a31ba012bc5ddc2f2157e0059429960409997610e57966125b0565b600154166125b0565b8351928352820152a180f35b94509492908185813d8311610e99575b610e7d8183611b9c565b81010312610e95579351929492939092610e57610e16565b8680fd5b503d610e73565b6040513d89823e3d90fd5b9095508381813d8311610ed3575b610ec38183611b9c565b81010312610e9557519438610ded565b503d610eb9565b610ef990823d8411610f00575b610ef18183611b9c565b8101906125f3565b5038610dba565b503d610ee7565b50346102e857806003193601126102e8576020610f226126e0565b6104c5612ab6565b50346102e857806003193601126102e85760206104c5612551565b50346102e857610f86604091610f5a36611c11565b6001600160a01b038116845260008051602061327d833981519152602052949092205490939290611fdd565b808411610fa757506104c59083610f9e602095611f32565b93849233612f27565b604051633fa733bb60e21b81526001600160a01b039390931660048401526024830193909352506044810191909152606490fd5b50346102e85760203660031901126102e85760206104c5600435611fa5565b50346102e857806003193601126102e8576007546040516001600160a01b039091168152602090f35b50346102e85760403660031901126102e85761104a611040611b06565b6024359033611d4b565b602060405160018152f35b50346102e85760203660031901126102e85760209060ff906040906001600160a01b03611080611b06565b168152600d84522054166040519015158152f35b50346102e857806003193601126102e8576040518160008051602061329d8339815191529081546110c481611d11565b9182855260209360019283811690816000146111495750600114611103575b6110ff866110f3818a0382611b9c565b60405191829182611a9a565b0390f35b815292945060008051602061331d8339815191525b82841061113657505050816110ff936110f3928201019338806110e3565b8054858501870152928501928101611118565b60ff19168787015250505050151560051b82010191506110f3816110ff38806110e3565b50346102e85760403660031901126102e85760206004356104c561118f611b1c565b9161119981611fa5565b809333612dbe565b50346102e857806003193601126102e8576001546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e8576000805160206132bd833981519152546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e85760206104c56123fe565b50346102e857806003193601126102e85760206104c5612484565b50346102e857806003193601126102e8576006546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e857611278611c64565b6000805160206132bd83398151915280546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346102e85760403660031901126102e85760206004356104c56112eb611b1c565b6112f483611f6d565b92839133612dbe565b50346102e857806003193601126102e8576002546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e8576003546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e857602062ffffff600c5416604051908152f35b50346102e85760403660031901126102e85761138c611b06565b602435906001600160401b038211611411573660238301121561141157828260040135926113b984611bbd565b6113c66040519182611b9c565b8481526020810190366024878501011161140d57836020876114099860248497018637830101526113f5611c64565b519082855af161140361211a565b9161267d565b5080f35b8380fd5b8280fd5b611ae3565b50346102e857806003193601126102e857546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e8576000805160206132fd833981519152546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e85760206104c56123bc565b50346102e85760403660031901126102e8576114ac611b06565b60243590811515809203611411577faf367c7d20ce5b2ab6da56afd0c9c39b00ba995263c60292a3e1ee3781fd4885916040916114e7611c64565b60018060a01b031690818552600d60205282852060ff1981541660ff831617905582519182526020820152a180f35b50346102e857806003193601126102e85760ff6000805160206132fd8339815191525460a01c169060ff821161155157602082604051908152f35b634e487b7160e01b81526011600452602490fd5b50346102e857806003193601126102e8576005546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e8576008546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e85760206104c56126e0565b50346102e85760603660031901126102e85761104a6115ef611b06565b6115f7611b1c565b60443591611606833383611e5c565b611d4b565b50346102e857806003193601126102e857600a546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e8576004546040516001600160a01b039091168152602090f35b50346102e857806003193601126102e85760206000805160206132dd83398151915254604051908152f35b50346102e857806003193601126102e85760206104c5612350565b50346102e857602080600319360112610413576116be611b06565b906116c7611c64565b6001600160a01b0382811691849190836117615750818080926000805160206132bd833981519152541647905af16116fd61211a565b50156117495750604080516001600160a01b0390921682524760208301527fdc0a5915a6b7c8261db17a13f9bde0836f7cb970ba0eb51255b272aac2ceaa6a9190819081015b0390a180f35b60249060405190638df9e3eb60e01b82526004820152fd5b929391506000805160206132bd8339815191525416604051906370a0823160e01b908183523060048401528483602481895afa908115610ea05785938892611873575b50906117b091876125b0565b60246040518096819382523060048301525afa918215611868578492611818575b50604080516001600160a01b03909216825260208201929092527fdc0a5915a6b7c8261db17a13f9bde0836f7cb970ba0eb51255b272aac2ceaa6a92509081908101611743565b90915082813d8311611861575b61182f8183611b9c565b81010312611411577fdc0a5915a6b7c8261db17a13f9bde0836f7cb970ba0eb51255b272aac2ceaa6a915190386117d1565b503d611825565b6040513d86823e3d90fd5b8092508491943d831161189e575b61188b8183611b9c565b81010312610e95575184926117b06117a4565b503d611881565b50346102e85760203660031901126102e85760206104c5600435611f32565b50346102e85760403660031901126102e8576118de611b06565b60243590331561195c576001600160a01b0316918215611944576040829161190533611bd8565b85825260205220556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b60249060405190634a1406b160e11b82526004820152fd5b60405163e602df0560e01b815260048101849052602490fd5b50346102e85760203660031901126102e85760043562ffffff81168091036104135760207f12984eff84f72590c390d6926f95b42889b00edf768a9fb98e9fc0e9954b385f916119c3611c64565b8062ffffff19600c541617600c55604051908152a180f35b50346102e857806003193601126102e8576040518160008051602061325d833981519152908154611a0b81611d11565b9182855260209360019283811690816000146111495750600114611a39576110ff866110f3818a0382611b9c565b815292945060008051602061323d8339815191525b828410611a6c57505050816110ff936110f3928201019338806110e3565b8054858501870152928501928101611a4e565b50346102e857806003193601126102e85760206104c56121d9565b6020808252825181830181905290939260005b828110611acf57505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611aad565b34611b01576020366003190112611b015760206104c5600435611fdd565b600080fd5b600435906001600160a01b0382168203611b0157565b602435906001600160a01b0382168203611b0157565b34611b01576020366003190112611b0157611b4b611b06565b5060206040516000198152f35b6001600160401b038111611b6b57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b03821117611b6b57604052565b90601f801991011681019081106001600160401b03821117611b6b57604052565b6001600160401b038111611b6b57601f01601f191660200190565b6001600160a01b031660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020526040902090565b6060906003190112611b0157600435906001600160a01b03906024358281168103611b0157916044359081168103611b015790565b34611b01576020366003190112611b015760206104c5600435611f6d565b6000805160206132bd833981519152546001600160a01b03163303611c8557565b60405163118cdaa760e01b8152336004820152602490fd5b6001600160a01b03908116908115611cf8576000805160206132bd83398151915280546001600160a01b031981168417909155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b604051631e4fbdf760e01b815260006004820152602490fd5b90600182811c92168015611d41575b6020831014611d2b57565b634e487b7160e01b600052602260045260246000fd5b91607f1691611d20565b916001600160a01b03808416928315611e205716928315611e075760009083825260008051602061327d83398151915280602052604083205491848310611dd4575082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef959360409388602097528652038282205586815220818154019055604051908152a3565b60405163391434e360e21b81526001600160a01b0391909116600482015260248101929092525060448101839052606490fd5b60405163ec442f0560e01b815260006004820152602490fd5b604051634b637e8f60e11b815260006004820152602490fd5b91908201809211611e4657565b634e487b7160e01b600052601160045260246000fd5b9190611e6783611bd8565b9260018060a01b0390818316916000958387526020526040862054936000198503611e96575b50505050505050565b858510611f015750811615611ee8578115611ecf5790611eb96040949392611bd8565b9085526020520391205538808080808080611e8d565b604051634a1406b160e11b815260048101869052602490fd5b60405163e602df0560e01b815260048101869052602490fd5b604051637dc7a0d960e11b81526001600160a01b039190911660048201526024810185905260448101869052606490fd5b6000805160206132dd8339815191525460018101809111611e4657611f556121d9565b9060018201809211611e4657611f6a92612015565b90565b6000805160206132dd8339815191525460018101809111611e4657611f906121d9565b9060018201809211611e4657611f6a92612055565b611fad6121d9565b60018101809111611e46576000805160206132dd833981519152549060018201809211611e4657611f6a92612015565b611fe56121d9565b60018101809111611e46576000805160206132dd833981519152549060018201809211611e4657611f6a92612055565b9190612022828285612055565b92821561203f57096120315790565b60018101809111611e465790565b634e487b7160e01b600052601260045260246000fd5b909182820291600019848209938380861095039480860395146120dd57848311156120cb5782910981600003821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b60405163227bc15360e01b8152600490fd5b50508092501561203f570490565b60ff60008051602061333d8339815191525460401c161561210857565b604051631afcd79f60e31b8152600490fd5b3d15612145573d9061212b82611bbd565b916121396040519384611b9c565b82523d6000602084013e565b606090565b90604051602081019063313ce56760e01b82526004815261216a81611b81565b5160009384928392916001600160a01b03165afa61218661211a565b90806121ba575b612197575b508190565b602081805181010312611411576020015160ff8111612192576001925060ff1690565b5060208151101561218d565b81810292918115918404141715611e4657565b6121e1612350565b600354604051630176f71760e71b81526001600160a01b039260209290919083908290600490829088165afa8015612314578391600091612320575b506122386c0c9f2c9cd04674edea40000000916024946121c6565b049360005416604051928380926370a0823160e01b82523060048301525afa918215612314576000926122e1575b50506122a561227b61229e926122b994611e39565b6122836123bc565b68056bc75e2d63100000938491612298612551565b906121c6565b0490611e39565b9061229e6122b16123fe565b612298612484565b62ffffff80600c541691620f424092830391808311611e46576122dd9216906121c6565b0490565b81819392933d831161230d575b6122f88183611b9c565b810103126102e85750516122a561227b612266565b503d6122ee565b6040513d6000823e3d90fd5b9182813d8311612349575b6123358183611b9c565b810103126102e8575051829061223861221d565b503d61232b565b6006546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156123145760009161238e575090565b906020823d82116123b4575b816123a760209383611b9c565b810103126102e857505190565b3d915061239a565b6007546001600160a01b031660206123d26123fe565b60246040518094819363323808cd60e01b835260048301525afa9081156123145760009161238e575090565b6006546040516246613160e11b815230600482015290602090829060249082906001600160a01b03165afa9081156123145760009161238e575090565b519069ffffffffffffffffffff82168203611b0157565b908160a0910312611b01576124668161243b565b91602082015191604081015191611f6a60806060840151930161243b565b600a54604051633fabe5a360e21b81529060a090829060049082906001600160a01b03165afa801561231457600080928190829361251a575b50600084131561250857156124f65769ffffffffffffffffffff8091169116106124e45790565b6040516320a65f7560e01b8152600490fd5b604051630bdd549360e01b8152600490fd5b60405163100da7c960e31b8152600490fd5b9250505061253f915060a03d811161254a575b6125378183611b9c565b810190612452565b9293919050386124bd565b503d61252d565b600954604051633fabe5a360e21b81529060a090829060049082906001600160a01b03165afa801561231457600080928190829361251a5750600084131561250857156124f65769ffffffffffffffffffff8091169116106124e45790565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526125f1916125ec606483611b9c565b61260b565b565b90816020910312611b0157518015158103611b015790565b6000806126349260018060a01b03169360208151910182865af161262d61211a565b908361267d565b8051908115159182612662575b505061264a5750565b60249060405190635274afe760e01b82526004820152fd5b61267592506020809183010191016125f3565b153880612641565b906126a4575080511561269257805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806126d7575b6126b5575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156126ad565b60009060003381526020600d815260409060ff82842054161561299c576006548251631e8c5c8960e11b81526001600160a01b0391839082906004908290899087165af1801561299257612975575b5060025483516370a0823160e01b8082523060048301529183168482602481845afa91821561293c578792612946575b5084846001541693602488518096819382523060048301525afa92831561293c57879361290d575b5081151580612904575b61279e5750505050505050565b600854865163095ea7b360e01b8082529186166001600160a01b031660048201526024810184905297995095979694959394929391929086908290818c816044810103925af180156128fa5761282e92879286926128dd575b506001546008548b5192835288166001600160a01b03166004830152602482019290925292839187169082908c9082906044820190565b03925af180156128d3579261289e927fc8004e996aeb9ddf998debb2ef732f1fc90ad7ef353fc45c0559b81497429904979899926128a496956128b6575b5061288a8560025416868554169262ffffff9384600c5416926129b3565b94806001541693541690600c5416926129b3565b90611e39565b9351848152a138808080808080611e8d565b6128cc90883d8a11610f0057610ef18183611b9c565b503861286c565b87513d8a823e3d90fd5b6128f390843d8611610f0057610ef18183611b9c565b50386127f7565b88513d8b823e3d90fd5b50821515612791565b9092508481813d8311612935575b6129258183611b9c565b81010312610e9557519138612787565b503d61291b565b86513d89823e3d90fd5b9091508481813d831161296e575b61295e8183611b9c565b81010312610e955751903861275f565b503d612954565b61298b90833d8511610f0057610ef18183611b9c565b503861272f565b84513d87823e3d90fd5b81516370cd133960e11b8152336004820152602490fd5b60405192939161010084016001600160401b03811185821017611b6b57604052600160a01b600190038092168452816020850191168152816040850162ffffff80951681526060860196308852608087019042825260a0880190815260c08801918460009a8b998a865260e08c01968b88528380600854169a6040519e8f9d8e9163414bf38960e01b835251169060040152511660248c0152511660448a0152511660648801525160848701525160a48601525160c4850152511660e4830152815a9361010492602095f1918215612aaa578092612a9057505090565b9091506020823d82116123b457816123a760209383611b9c565b604051903d90823e3d90fd5b60008054604080516370a0823160e01b80825230600480840191909152602095909490926001600160a01b03929183168782602481845afa918215612d2d578793929189918793612d84575b508454885163095ea7b360e01b8082529188166001600160a01b0316968101968752602087018590529095909291839182908a9082906040015b03925af18015612cfa57612d67575b5083875416846003541691875190608082018281106001600160401b03821117612d54578952878252818b018890528189015260608101879052875163384e03db60e01b8152898101939093528991839188919082602485015b8d8210612d375750505081839160c49360a48401525af18015612d2d57908891612d04575b505082600354168360055416865192835230888401528883602481855afa928315612cfa5790868a9493928194612cbe575b5088519586526001600160a01b039091168986019081526020810193909352849283919082906040015b03925af18015612cb45790869291612c97575b50600554600b54855163303acfe760e11b81529687015260016024870152859160449183918691165af1918215612c8d575050612c74575050565b81612c8a92903d10610f0057610ef18183611b9c565b50565b51903d90823e3d90fd5b612cad90833d8511610f0057610ef18183611b9c565b5038612c39565b84513d85823e3d90fd5b9294809294508391503d8311612cf3575b612cd98183611b9c565b81010312612cef57518892909186612c26612bfc565b8580fd5b503d612ccf565b87513d88823e3d90fd5b813d8311612d26575b612d178183611b9c565b8101031261140d578638612bca565b503d612d0d565b86513d87823e3d90fd5b825181528e968896508c9550928301926001929092019101612ba5565b634e487b7160e01b895260418b52602489fd5b612d7d90893d8b11610f0057610ef18183611b9c565b5038612b4b565b925092935081813d8311612db7575b612d9d8183611b9c565b81010312612db357518692918890612b3c612b02565b8480fd5b503d612d93565b91600093338552600d60205260409260ff848720541615612f10576000805160206132fd8339815191525484516323b872dd60e01b60208201526001600160a01b039687166024820181905230604483015260648083018790528252969291831660a082016001600160401b03811183821017612efc578752612e41919061260b565b16948515612ee55790857fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79493926000805160206132dd833981519152612e89848254611e39565b905581815260008051602061327d8339815191526020528481208381540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208651858152a382519182526020820152a36125f1612ab6565b60249084519063ec442f0560e01b82526004820152fd5b634e487b7160e01b8a52604160045260248afd5b83516370cd133960e11b8152336004820152602490fd5b9094929193600094338652602096600d885260409260ff848920541615612f10578798612f559897986126e0565b506006546001600160a01b0394908516803b156132385789875180926324f81cd160e11b825260049e8f83015281835a92602493f180156131bd57613225575b5060035486516370a0823160e01b8152308d8201529086168382602481845afa9182156131e457908d8c869381956131ee575b5081548b5163095ea7b360e01b8152908b166001600160a01b031692810192835260208301869052939284929091839182906040015b03925af180156131e4576131c7575b50828c608488825416918d8a60035416938c5196879586946314f6943160e11b86528501526024840152600260448401528160648401525af180156131bd57908391613194575b50508481169885881697848b8a03613183575b5050891561316d57508860005260008051602061327d8339815191529081835286600020549b848d1061313957505088999a83917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db98999a60005283520385600020556000805160206132dd8339815191528281540390556000897fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef838851868152a36131268884866000805160206132fd83398151915254166125b0565b84519788528701521693a46125f1612ab6565b875163391434e360e21b81526001600160a01b03909216908201908152602081018d90526040810185905281906060010390fd5b8b602491885191634b637e8f60e11b8352820152fd5b61318d9184611e5c565b3884613067565b813d83116131b6575b6131a78183611b9c565b81010312611b01578138613054565b503d61319d565b87513d8c823e3d90fd5b6131dd90843d8611610f0057610ef18183611b9c565b503861300d565b88513d8d823e3d90fd5b93929450505081813d831161321e575b6132088183611b9c565b81010312611b0157519083908d8c612ffe612fc8565b503d6131fe565b61323190999199611b58565b9738612f95565b8980fdfe2ae08a8e29253f69ac5d979a101956ab8f8d9d7ded63fa7a83b16fc47648eab052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0352c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace049016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace020773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e0046a2803e59a4de4e7a4c574b1243f25977ac4c77d5a1a4a609b5394cebb4a2aaf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212205874d2c1ad16fd0c72fe5cc8f2317bfadb4a7200ccd11d6ebae6d8c2a2fdb22364736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: paris Optimization: Yes (200 runs)
ICurvePool.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface ICurvePool {
    function get_virtual_price() external view returns (uint256);
    function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 min_received)
        external
        returns (uint256);
}
ISwapRouter.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface ISwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint deadline;
        uint amountIn;
        uint amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps amountIn of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as ExactInputSingleParams in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(
        ExactInputSingleParams calldata params
    ) external payable returns (uint amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint deadline;
        uint amountIn;
        uint amountOutMinimum;
    }

    /// @notice Swaps amountIn of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as ExactInputParams in calldata
    /// @return amountOut The amount of the received token
    function exactInput(
        ExactInputParams calldata params
    ) external payable returns (uint amountOut);
}
IMetaPoolZap.sol 30 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.21;

// See https://etherscan.io/address/0xa79828df1850e8a3a3064576f380d90aecdd3359#code for an example
/*  solhint-disable func-name-mixedcase, var-name-mixedcase */
interface I3CrvMetaPoolZap {
    function add_liquidity(address pool, uint256[4] memory depositAmounts, uint256 minMintAmount)
        external
        returns (uint256);

    function add_liquidity(address pool, uint256[4] memory depositAmounts, uint256 minMintAmount, address receiver)
        external
        returns (uint256);

    function remove_liquidity_one_coin(address pool, uint256 burnAmount, int128 index, uint256 minAmount)
        external
        returns (uint256);

    function remove_liquidity_imbalance(address _pool, uint256[4] memory _amounts, uint256 _maxBurnAmount)
        external
        returns (uint256);

    function calc_withdraw_one_coin(address pool, uint256 tokenAmount, int128 index) external view returns (uint256);

    function calc_token_amount(address _pool, uint256[4] memory _deposit_amounts, bool _is_deposit)
        external
        view
        returns (uint256);
}
/*  solhint-disable func-name-mixedcase, var-name-mixedcase */
BaseRewardPool.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface BaseRewardPool {
    function balanceOf(address account) external view returns (uint256);
    function getReward() external returns (bool);
    function withdrawAllAndUnwrap(bool claim) external;
    function withdrawAndUnwrap(uint256 amount, bool claim) external;
    function earned(address account) external view returns (uint256);
}
IConvexBooster.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IConvexBooster {
    function depositAll(uint256 _pid, bool _stake) external returns (bool);
    function withdraw(uint256 _pid, uint256 _amount) external;
    function withdrawAll(uint256 _pid) external returns (bool);
    function withdrawTo(uint256 _pid, uint256 _amount, address _to) external returns(bool);
}
IConvexHandler.sol 11 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.21;

interface IConvexHandler {
    function deposit(address _curvePool, uint256 _amount) external;
    function claimBatchEarnings(address[] memory _curvePools, address _conicPool) external;
    function getRewardPool(address _curvePool) external view returns (address);
    function getCrvEarned(address _account, address _curvePool) external view returns (uint256);
    function getCrvEarnedBatch(address _account, address[] memory _curvePools) external view returns (uint256);
    function computeClaimableConvex(uint256 crvAmount) external view returns (uint256);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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 AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
Math.sol 415 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}
AggregatorV3Interface.sol 21 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);
    function description() external view returns (string memory);
    function version() external view returns (uint256);

    // getRoundData and latestRoundData should both raise "No data present"
    // if they do not have data to report, instead of returning unset values
    // which could be misinterpreted as actual reported values.
    function getRoundData(uint80 _roundId)
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
IERC4626.sol 230 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
ReturnFinanceConvexUSDCVault.sol 364 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";

import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
import {IConvexBooster} from "../interfaces/IConvexBooster.sol";
import {IConvexHandler} from "../interfaces/IConvexHandler.sol";
import {ICurvePool} from "../interfaces/ICurvePool.sol";
import {BaseRewardPool} from "../interfaces/BaseRewardPool.sol";
import {I3CrvMetaPoolZap} from "../interfaces/IMetaPoolZap.sol";
import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol";
import {IReturnFinanceConvexUSDCVault} from "../interfaces/IReturnFinanceConvexUSDCVault.sol";

/**
 * @title Return Finance Convex USDC Vault
 * @author Return Finance
 * @dev Return Finance Convex USDC Vault is an ERC4626 compliant vault.
 * @dev The ERC4626 "Tokenized Vault Standard" is defined in https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 */
contract ReturnFinanceConvexUSDCVault is IReturnFinanceConvexUSDCVault, ERC4626Upgradeable, OwnableUpgradeable {
    using SafeERC20 for IERC20;
    using Address for address;

    /* ========== STATE VARIABLES ========== */

    address public usdc;
    address public cvx;
    address public crv;
    address public curveLpToken;
    address public curveDepositZap;
    address public convexBooster;
    address public convexRewards;
    address public convexHandler;
    address public uniswapV3Router;
    address public chainlinkDataFeedCVXUSD;
    address public chainlinkDataFeedCRVUSD;

    uint256 public convexPoolId;
    uint24 public uniswapFee;

    /**
     * @notice Represents the whitelist of addresses that can interact with this contract
     */
    mapping(address => bool) public whitelist;

    /**
     * @notice Function to receive ether, which emits a donation event
     */
    receive() external payable {
        emit PoolDonation(_msgSender(), msg.value);
    }

    /* ========== CONSTRUCTOR ========== */

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /**
     * @dev Initialize the Return Finance Convex USDC Vault.
     * @param config Configuration struct
     */
    function initialize(Config memory config) external initializer {
        __Ownable_init(_msgSender());
        __ERC4626_init(config.usdc);
        __ERC20_init("Return Finance Convex USDC Vault", "rfCvxUSDC");

        usdc = address(config.usdc);
        cvx = config.cvx;
        crv = config.crv;
        curveLpToken = config.curveLpToken;
        curveDepositZap = config.curveDepositZap;
        convexBooster = config.convexBooster;
        convexRewards = config.convexRewards;
        convexHandler = config.convexHandler;
        convexPoolId = config.convexPoolId;
        uniswapFee = config.uniswapFee;
        uniswapV3Router = config.uniswapV3Router;
        chainlinkDataFeedCVXUSD = config.chainlinkDataFeedCVXUSD;
        chainlinkDataFeedCRVUSD = config.chainlinkDataFeedCRVUSD;
    }

    /* ========== VIEWS ========== */

    /**
     * @dev See {IERC4626-totalAssets}.
     * @notice The sum of total assets controlled by the vault is calclulated as follows:
     * ::Curve LP tokens staked in Convex and denominated in USDC
     * ::USDC in the vault (if any)
     * ::Accrued CRV denomiated in USD
     * ::Accrued CVX denominated in USD
     * The amount is reduced by 1%, to account for Uniswap fees/slippage
     */
    function totalAssets() public view override returns (uint256) {
        return (
            (stakedLpInConvex() * ICurvePool(curveLpToken).get_virtual_price() / 1e30)
                + IERC20(usdc).balanceOf(address(this)) + (earnedCVX() * uint256(cvxPriceUSD()) / 1e20)
                + (earnedCRV() * uint256(crvPriceUSD()) / 1e20)
        ) * (1000000 - uniswapFee) / 1000000;
    }

    /**
     * @notice The staked Curve LP tokens in Convex
     * @return The amount of staked Curve LP tokens in Convex Finance
     */
    function stakedLpInConvex() public view returns (uint256) {
        return BaseRewardPool(convexRewards).balanceOf(address(this));
    }

    /**
     * @notice The accrued CVX rewards from staking Curve LP tokens in Convex Finance
     * @return The amount of CVX rewards accrued in the protocol
     */
    function earnedCVX() public view returns (uint256) {
        return IConvexHandler(convexHandler).computeClaimableConvex(earnedCRV());
    }

    /**
     * @notice The accrued CRV rewards from staking Curve LP tokens in Convex Finance
     * @return The amount of CRV rewards accrued in the protocol
     */
    function earnedCRV() public view returns (uint256) {
        return BaseRewardPool(convexRewards).earned(address(this));
    }

    /**
     * @notice The CRV/USD price
     * @return The price of CRV denominated in USD
     */
    function crvPriceUSD() public view returns (int256) {
        (uint80 roundId, int256 answer, uint256 startedAt,, uint80 answeredInRound) =
            AggregatorV3Interface(chainlinkDataFeedCRVUSD).latestRoundData();
        if (answer <= 0) revert ChainlinkPriceZero();
        if (startedAt == 0) revert ChainlinkIncompleteRound();
        if (answeredInRound < roundId) revert ChainlinkStalePrice();

        return answer;
    }

    /**
     * @notice The CVX/USD price
     * @return The price of CVX denominated in USD
     */
    function cvxPriceUSD() public view returns (int256) {
        (uint80 roundId, int256 answer, uint256 startedAt,, uint80 answeredInRound) =
            AggregatorV3Interface(chainlinkDataFeedCVXUSD).latestRoundData();
        if (answer <= 0) revert ChainlinkPriceZero();
        if (startedAt == 0) revert ChainlinkIncompleteRound();
        if (answeredInRound < roundId) revert ChainlinkStalePrice();

        return answer;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    /**
     * @notice Send all tokens or ETH held by the contract to the owner
     * @param token The token to sweep, or 0 for ETH
     */
    function sweepFunds(address token) external onlyOwner {
        if (token == address(0)) {
            (bool success,) = owner().call{value: address(this).balance}("");
            if (!success) revert UnableToSweep(token);
            emit SweepFunds(token, address(this).balance);
        } else {
            IERC20(token).safeTransfer(owner(), IERC20(token).balanceOf(address(this)));
            emit SweepFunds(token, IERC20(token).balanceOf(address(this)));
        }
    }

    /**
     * @notice Rescue any locked funds from the pools
     * @param destination The address where the funds should be sent
     */
    function rescueFunds(address destination) external onlyOwner {
        uint256 totalLP = stakedLpInConvex();
        BaseRewardPool(convexRewards).withdrawAndUnwrap(totalLP, false);
        IERC20(curveLpToken).safeTransfer(destination, totalLP);

        emit RescueFunds(totalLP);
    }

    /**
     * @notice Rescue any locked rewards
     * @param destination The address where the funds should be sent
     */
    function rescueRewards(address destination) external onlyOwner {
        // Claim pending CVX and CRV rewards from Convex
        BaseRewardPool(convexRewards).getReward();

        uint256 crvRewards = IERC20(crv).balanceOf(address(this));
        uint256 cvxRewards = IERC20(cvx).balanceOf(address(this));

        IERC20(crv).safeTransfer(destination, crvRewards);
        IERC20(cvx).safeTransfer(destination, cvxRewards);

        emit RescueRewards(crvRewards, cvxRewards);
    }

    /**
     * @notice Updates the swap fee used in _swap and totalAssets calculation
     * @param newSwapFee The new swap fee
     */
    function updateSwapFee(uint24 newSwapFee) external onlyOwner {
        uniswapFee = newSwapFee;

        emit SwapFeeUpdated(newSwapFee);
    }

    /**
     * @notice Allow or disallow an address to interact with the contract
     * @param updatedAddress The address to change the whitelist status for
     * @param isWhitelisted Whether the address should be whitelisted
     */
    function toggleWhitelist(address updatedAddress, bool isWhitelisted) external onlyOwner {
        whitelist[updatedAddress] = isWhitelisted;

        emit AddressWhitelisted(updatedAddress, isWhitelisted);
    }

    /**
     * @notice Allow the owner to call an external contract for some reason. E.g. claim an airdrop.
     * @param target The target contract address
     * @param data Encoded function data
     */
    function callExternalContract(address target, bytes memory data) external onlyOwner {
        target.functionCall(data);
    }

    /**
     * @notice Swap function for the underlying token (USDC) and DAI
     * @param tokenIn The address of the token to be swapped
     * @param tokenOut The address of the token to be received
     * @param amountIn The amount of token to be swapped
     * @param amountOutMinimum The minimum amount of token to be received
     * @param swapFee The swap fee
     * @return amountOut The amount of tokens received from the swap
     */
    function _swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMinimum, uint24 swapFee)
        internal
        returns (uint256 amountOut)
    {
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            fee: swapFee,
            recipient: address(this),
            deadline: block.timestamp,
            amountIn: amountIn,
            amountOutMinimum: amountOutMinimum,
            sqrtPriceLimitX96: 0
        });

        amountOut = ISwapRouter(uniswapV3Router).exactInputSingle(params);
    }

    /**
     * @notice Claims accrued CRV and CVX rewards and swaps them for USDC on Uniswap V3
     * @return harvestAmount The amount harvested denominated in USDC
     */
    function harvestRewards() public returns (uint256 harvestAmount) {
        if (!whitelist[_msgSender()]) revert NotInWhitelist(_msgSender());

        // Claim pending CVX and CRV rewards from Convex Finance
        BaseRewardPool(convexRewards).getReward();

        uint256 crvRewards = IERC20(crv).balanceOf(address(this));
        uint256 cvxRewards = IERC20(cvx).balanceOf(address(this));

        // Swap only if there's anything to swap
        if (crvRewards > 0 && cvxRewards > 0) {
            // Approve Uniswap V3 Router to move CRV and CVX
            IERC20(crv).approve(uniswapV3Router, crvRewards);
            IERC20(cvx).approve(uniswapV3Router, cvxRewards);
            // Swap CRV and CVX for USDC
            uint256 usdcReceivedFromCRV = _swap(crv, usdc, crvRewards, 0, uniswapFee);
            uint256 usdcReceivedFromCVX = _swap(cvx, usdc, cvxRewards, 0, uniswapFee);

            harvestAmount = usdcReceivedFromCRV + usdcReceivedFromCVX;

            emit HarvestRewards(harvestAmount);
        }
    }
    /**
     * @notice Harvests rewards and deposits back to Convex
     * @return harvestAmount The amount harvested denominated in USDC
     */

    function harvestAndDepositRewards() external returns (uint256 harvestAmount) {
        harvestAmount = harvestRewards();
        _afterDepositOrWithdraw();
    }

    /**
     * @notice Hook called before a user withdraws USDC from the vault.
     */
    function _beforeWithdraw() internal {
        // Harvest rewards -> Claim CRV and CVX, swap it for USDC
        harvestRewards();

        // Because there is not accurate way to obtain the exact amount of USDC equivalent to the Curve LP tokens,
        // we withdraw and unwrap all Curve LP tokens from Convex. The remaining difference is deposited back in the
        // _afterDepositOrWithdraw() method
        BaseRewardPool(convexRewards).withdrawAllAndUnwrap(false);

        // Get the amount of LP tokens after withdraw form Convex
        uint256 lpTokensToWithdraw = IERC20(curveLpToken).balanceOf(address(this));

        // Approve Deposit Zap contract to move Curve LP tokens
        IERC20(curveLpToken).approve(curveDepositZap, lpTokensToWithdraw);

        // Remove liquidity from Curve and receive the underlying token (USDC)
        I3CrvMetaPoolZap(curveDepositZap).remove_liquidity_one_coin(curveLpToken, lpTokensToWithdraw, 2, 0);
    }

    /**
     * @notice Hook called after a user withdraws USDC from the vault.
     */
    function _afterDepositOrWithdraw() internal {
        // Get remaining USDC in the pool
        uint256 assetsToDeposit = IERC20(usdc).balanceOf(address(this));

        // Approve the Deposit Zap contract to spend USDC
        IERC20(usdc).approve(curveDepositZap, assetsToDeposit);

        // Add the availanle USDC as liquidity to Curve
        I3CrvMetaPoolZap(curveDepositZap).add_liquidity(curveLpToken, [0, 0, assetsToDeposit, 0], 0);

        // Approve Convex Booster contract to move Curve LP tokens
        IERC20(curveLpToken).approve(convexBooster, IERC20(curveLpToken).balanceOf(address(this)));

        // Deposit the Curve LP tokens to Convex Finance
        IConvexBooster(convexBooster).depositAll(convexPoolId, true);
    }

    /**
     * @dev See {ERC4626-_deposit}.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override {
        if (!whitelist[_msgSender()]) revert NotInWhitelist(_msgSender());
        super._deposit(caller, receiver, assets, shares);
        _afterDepositOrWithdraw();
    }

    /**
     * @dev See {ERC4626-_withdraw}.
     */
    function _withdraw(address caller, address receiver, address owner, uint256 assets, uint256 shares)
        internal
        override
    {
        if (!whitelist[_msgSender()]) revert NotInWhitelist(_msgSender());
        _beforeWithdraw();
        super._withdraw(caller, receiver, owner, assets, shares);
        _afterDepositOrWithdraw();
    }
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 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 ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-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 ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 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);
}
IReturnFinanceConvexUSDCVault.sol 42 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IReturnFinanceConvexUSDCVault {
    struct Config {
        IERC20 usdc;
        address cvx;
        address crv;
        address curveLpToken;
        address curveDepositZap;
        address convexBooster;
        address convexRewards;
        address convexHandler;
        uint256 convexPoolId;
        uint24 uniswapFee;
        address uniswapV3Router;
        address chainlinkDataFeedCVXUSD;
        address chainlinkDataFeedCRVUSD;
    }

    event SweepFunds(address token, uint256 amount);
    event PoolDonation(address sender, uint256 value);
    event AddressWhitelisted(address whitelistedAddress, bool isWhitelisted);
    event RescueFunds(uint256 totalUsdc);
    event RescueRewards(uint256 crvRewards, uint256 cvxRewards);
    event SlippageUpdated(uint256 newSlippage);
    event SetHarvestRewards(bool harvest);
    event HarvestRewards(uint256 amount);
    event SwapFeeUpdated(uint24 newSwapFee);

    function sweepFunds(address token) external;
    function rescueFunds(address destination) external;
    function toggleWhitelist(address updatedAddress, bool isWhitelisted) external;

    error UnableToSweep(address token);
    error NotInWhitelist(address wrongAddress);
    error ChainlinkPriceZero();
    error ChainlinkIncompleteRound();
    error ChainlinkStalePrice();
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
ContextUpgradeable.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    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;
    }
}
OwnableUpgradeable.sol 119 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
    struct OwnableStorage {
        address _owner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        OwnableStorage storage $ = _getOwnableStorage();
        return $._owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        OwnableStorage storage $ = _getOwnableStorage();
        address oldOwner = $._owner;
        $._owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
Initializable.sol 228 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20 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);
}
ERC20Upgradeable.sol 341 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.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 ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
    /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
    struct ERC20Storage {
        mapping(address account => uint256) _balances;

        mapping(address account => mapping(address spender => uint256)) _allowances;

        uint256 _totalSupply;

        string _name;
        string _symbol;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;

    function _getERC20Storage() private pure returns (ERC20Storage storage $) {
        assembly {
            $.slot := ERC20StorageLocation
        }
    }

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        ERC20Storage storage $ = _getERC20Storage();
        $._name = name_;
        $._symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        ERC20Storage storage $ = _getERC20Storage();
        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) {
        ERC20Storage storage $ = _getERC20Storage();
        return $._totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        ERC20Storage storage $ = _getERC20Storage();
        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) {
        ERC20Storage storage $ = _getERC20Storage();
        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}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * 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 {
        ERC20Storage storage $ = _getERC20Storage();
        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:
     * ```
     * 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 {
        ERC20Storage storage $ = _getERC20Storage();
        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);
            }
        }
    }
}
ERC4626Upgradeable.sol 311 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
 * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
 * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
 * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
 * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
 * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
 * expensive than it is profitable. More details about the underlying math can be found
 * xref:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626 {
    using Math for uint256;

    /// @custom:storage-location erc7201:openzeppelin.storage.ERC4626
    struct ERC4626Storage {
        IERC20 _asset;
        uint8 _underlyingDecimals;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC4626")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC4626StorageLocation = 0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00;

    function _getERC4626Storage() private pure returns (ERC4626Storage storage $) {
        assembly {
            $.slot := ERC4626StorageLocation
        }
    }

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    function __ERC4626_init(IERC20 asset_) internal onlyInitializing {
        __ERC4626_init_unchained(asset_);
    }

    function __ERC4626_init_unchained(IERC20 asset_) internal onlyInitializing {
        ERC4626Storage storage $ = _getERC4626Storage();
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        $._underlyingDecimals = success ? assetDecimals : 18;
        $._asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20Upgradeable) returns (uint8) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return $._underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return address($._asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        ERC4626Storage storage $ = _getERC4626Storage();
        return $._asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        ERC4626Storage storage $ = _getERC4626Storage();
        // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom($._asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        ERC4626Storage storage $ = _getERC4626Storage();
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer($._asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}

Read Contract

allowance 0xdd62ed3e → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
chainlinkDataFeedCRVUSD 0x1d87e7c3 → address
chainlinkDataFeedCVXUSD 0xfa64458d → address
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
convexBooster 0x2cdacb50 → address
convexHandler 0xb050ecb8 → address
convexPoolId 0xe529ee95 → uint256
convexRewards 0x79054cc9 → address
crv 0x6a4874a1 → address
crvPriceUSD 0x7b67ce45 → int256
curveDepositZap 0x1ac460ca → address
curveLpToken 0x646780df → address
cvx 0x923c1d61 → address
cvxPriceUSD 0xb48b6888 → int256
decimals 0x313ce567 → uint8
earnedCRV 0x8520a343 → uint256
earnedCVX 0x35dfce9c → uint256
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
stakedLpInConvex 0x157a7f20 → uint256
symbol 0x95d89b41 → string
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256
uniswapFee 0x637af4df → uint24
uniswapV3Router 0x2c76d7a6 → address
usdc 0x3e413bee → address
whitelist 0x9b19251a → bool

Write Contract 18 functions

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

approve 0x095ea7b3
address spender
uint256 value
returns: bool
callExternalContract 0x5e7b5fbf
address target
bytes data
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
harvestAndDepositRewards 0xb54a31a6
No parameters
returns: uint256
harvestRewards 0x2be11ae2
No parameters
returns: uint256
initialize 0x588570a5
tuple config
mint 0x94bf804d
uint256 shares
address receiver
returns: uint256
redeem 0xba087652
uint256 shares
address receiver
address owner
returns: uint256
renounceOwnership 0x715018a6
No parameters
rescueFunds 0xe53b2017
address destination
rescueRewards 0xb90817f2
address destination
sweepFunds 0x0fe28908
address token
toggleWhitelist 0x349123d1
address updatedAddress
bool isWhitelisted
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool
transferOwnership 0xf2fde38b
address newOwner
updateSwapFee 0x08ba9b65
uint24 newSwapFee
withdraw 0xb460af94
uint256 assets
address receiver
address owner
returns: uint256

Recent Transactions

No transactions found for this address