Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x75EF8d7366285857982d8443110ececb1930853C
Balance 0 ETH
Nonce 1
Code Size 10546 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

10546 bytes
0x60806040526004361015610018575b361561001657005b005b5f3560e01c80630bc8e05f146102c75780631626ba7e146102c257806316c38b3c146102bd5780631be19560146102b85780631d3c2b2f146102b3578063260ee14d146102ae5780632a5b5d13146102a95780632b7ac3f3146102a45780633a16b85a1461029f57806349bfe4111461029a5780635834d435146102955780635c975abb146102905780635f477c571461028b5780636052970c1461028657806363f2615d14610281578063645210701461027c578063715018a6146102775780637487432314610272578063759cb53b1461026d578063858a43d11461026857806389476069146102635780638cedddb51461025e5780638d68f9ff146102595780638da5cb5b146102545780639281aa0b1461024f57806392f25ea01461024a57806397fc007c146102455780639bec828814610240578063a67853481461023b578063a91ee0dc14610236578063a98a930414610231578063abc0ab511461022c578063b17fb9d614610227578063bbea097414610222578063c53b573d1461021d578063ce62d8eb14610218578063d936547e14610213578063e6a17b9a1461020e578063eb2578ef14610209578063edf187f014610204578063f2fde38b146101ff578063f9216f11146101fa5763fc6530c10361000e5761184c565b611824565b61175d565b6116d5565b61165e565b611636565b6115f6565b61157f565b6113ce565b6113b1565b61133a565b6112c0565b611298565b6111c8565b611174565b611130565b6110e9565b6110c1565b611069565b611041565b611019565b610e50565b610cdc565b610c98565b610c54565b610b9b565b610b40565b610b1b565b610af9565b610ad1565b6109a7565b610982565b61093e565b6108aa565b610833565b61080b565b6107e3565b610726565b610678565b6104a4565b61044f565b6103cd565b6102da565b5f9103126102d657565b5f80fd5b346102d6575f3660031901126102d6576003546102fc60ff8260a81c166118d2565b610304612306565b6001600160a01b0316801561038857600554610330906001600160a01b03165b6001600160a01b031690565b803b156102d65760405163d36f12fb60e01b81526001600160a01b0383166004820152905f908290818381602481015b03925af180156103835761037057005b8061037d610016926105df565b806102cc565b611917565b60405162461bcd60e51b815260206004820152601a60248201527f656d657267656e6379206d69677261746f72206e6f74207365740000000000006044820152606490fd5b346102d65760403660031901126102d6576001600160401b036024358181116102d657366023820112156102d65780600401359182116102d65736602483830101116102d657610441916024610426920160043561193a565b6040516001600160e01b031990911681529081906020820190565b0390f35b801515036102d657565b346102d65760203660031901126102d65760043561046c81610445565b610474612306565b6003805460ff60a81b191691151560a81b60ff60a81b16919091179055005b6001600160a01b038116036102d657565b346102d65760203660031901126102d6576004356104c181610493565b6104d360ff60035460a81c16156119e6565b61052260018060a01b0380928160015416331480156105b3575b6104f690611aba565b16917f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b16821415611aff565b600c5461053990610324906001600160a01b031681565b81146105a6575b6040516370a0823160e01b815230600482015290602082602481845afa90811561038357610016925f92610575575b506123a7565b61059891925060203d60201161059f575b610590818361062d565b810190611b43565b905f61056f565b503d610586565b6105ae61235e565b610540565b50335f908152600b602052604090205460ff166104ed565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b0381116105f257604052565b6105cb565b604081019081106001600160401b038211176105f257604052565b606081019081106001600160401b038211176105f257604052565b90601f801991011681019081106001600160401b038211176105f257604052565b6040519061065b82610612565b565b6001600160401b0381116105f257601f01601f191660200190565b346102d65760403660031901126102d6576004356001600160401b0381116102d657366023820112156102d65780600401356106b38161065d565b906106c1604051928361062d565b80825236602482850101116102d6576020815f92602461001696018386013783010152602435906106f182610445565b611b52565b9181601f840112156102d6578235916001600160401b0383116102d6576020808501948460051b0101116102d657565b346102d65760803660031901126102d65760043561074381610493565b6024359061075082610493565b606435916001600160401b0383116102d6576107736107b79336906004016106f6565b92909161078860ff60035460a81c16156119e6565b6107906124e3565b6001546001600160a01b0316331480156107bd575b6107ae90611aba565b60443591611d68565b60015f55005b50335f908152600b602052604090206107ae906107dc905b5460ff1690565b90506107a5565b346102d6575f3660031901126102d6576007546040516001600160a01b039091168152602090f35b346102d6575f3660031901126102d657600a546040516001600160a01b039091168152602090f35b346102d65760203660031901126102d6577f4868fa91d0121ea114513fb5c8b97b23ac9a10919980673c71461ff73e8501a3602060043561087381610493565b61087b612306565b6001600160a01b031661088f811515611f08565b600780546001600160a01b03191682179055604051908152a1005b346102d65760203660031901126102d6576004356001600160401b0381116102d6576108dd6107b79136906004016106f6565b906108f060ff60035460a81c16156119e6565b6108f86124e3565b6001546001600160a01b03163314801561091b575b61091690611aba565b6120dc565b50335f908152600b6020526040902061091690610937906107d5565b905061090d565b346102d65760203660031901126102d65760043561095b81610445565b610963612306565b6003805460ff60a01b191691151560a01b60ff60a01b16919091179055005b346102d6575f3660031901126102d657602060ff60035460a81c166040519015158152f35b346102d65760203660031901126102d6576004356109c481610493565b6109cc612306565b6001600160a01b0390808216906109e4821515611f08565b82600554168214610a9e57610a7f610a9992610a627f4923a3f3d9b7bd17a0d6fba89d92eba850a8f34176116be31bc27e12c4fef781957f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b16610a478582612537565b600554610a5c906001600160a01b0316610324565b90612614565b60018060a01b03166001600160601b0360a01b6005541617600555565b6040516001600160a01b0390911681529081906020820190565b0390a1005b60405162461bcd60e51b815260206004820152600b60248201526a105b1c9958591e481cd95d60aa1b6044820152606490fd5b346102d6575f3660031901126102d6576006546040516001600160a01b039091168152602090f35b346102d6575f3660031901126102d6576020610b1361228f565b604051908152f35b346102d6575f3660031901126102d657602060ff60035460a01c166040519015158152f35b346102d6575f3660031901126102d657610b58612306565b600180546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346102d65760203660031901126102d657600435610bb881610493565b610bc0612306565b7f906903afca224114603eddea72fbada90dcce859ee67b20861e842b79fc82ac060206001600160a01b038316610bf8811515611f08565b604051908152a1600854610c14906001600160a01b0316610324565b9060025490823b156102d6576040516317b0dca160e31b815260048101929092526001600160a01b03166024820152905f90829081838160448101610360565b346102d6575f3660031901126102d6576040517f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b6001600160a01b03168152602090f35b346102d6575f3660031901126102d6576040517f0000000000000000000000000655977feb2f289a4ab78af67bab0d17aab843676001600160a01b03168152602090f35b346102d6576020806003193601126102d657600435610cfa81610493565b610d026124e3565b610d1360ff60035460a81c166118d2565b610d1b612306565b6040516370a0823160e01b815230600482015282816024816001600160a01b0386165afa918215610383575f9283928391610e33575b5060405163a9059cbb60e01b86820190815233602483015260448201929092528390610d8a81606481015b03601f19810183528261062d565b51925af1610d96612260565b81610e03575b5015610dab5761001660015f55565b6084906040519062461bcd60e51b82526004820152602b60248201527f455243323048656c706572733a3a736166655472616e736665723a207472616e60448201526a1cd9995c8819985a5b195960aa1b6064820152fd5b80518015925083908315610e1b575b5050505f610d9c565b610e2b9350820181019101611922565b5f8281610e12565b610e4a9150853d871161059f57610590818361062d565b5f610d51565b346102d6575f3660031901126102d657610e686124e3565b600180546001600160a01b0390811633148015611001575b610e8990611aba565b610e916127aa565b600554610ea6906001600160a01b0316610324565b90813b156102d65760408051637050ccd960e01b81523060048201525f6024820181905290939192918490604490829084905af1928315610383578593610fee575b5090915f905b610f27575b610f1e847f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e16611a27565b61001660015f55565b8151811015610fe957908482610f50610f42839695856122f2565b51516001600160a01b031690565b7fc469fb3cff504b129a86c67aa7285a806d0a52ae22af6bbded54359b4f23292060209182610f7f85886122f2565b51015187514281526001600160a01b039290921660208301526040820152606090a180610fac83866122f2565b510151610fbe575b5001909192610eee565b610fe390610fcf610f4284876122f2565b90610fda84876122f2565b510151906123a7565b5f610fb4565b610ef3565b8061037d610ffb926105df565b5f610ee8565b50335f908152600b602052604090205460ff16610e80565b346102d6575f3660031901126102d6576005546040516001600160a01b039091168152602090f35b346102d6575f3660031901126102d6576001546040516001600160a01b039091168152602090f35b346102d65760403660031901126102d65760043561108681610493565b6024359061109382610445565b61109b612306565b60018060a01b03165f52600b60205260405f209060ff8019835416911515161790555f80f35b346102d6575f3660031901126102d6576009546040516001600160a01b039091168152602090f35b346102d65760203660031901126102d65760043561110681610493565b61110e612306565b600a80546001600160a01b0319166001600160a01b0392909216919091179055005b346102d6575f3660031901126102d6576040517f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e6001600160a01b03168152602090f35b346102d65760203660031901126102d65760043561119181610493565b600354906111a460ff8360a81c166118d2565b6111ac612306565b6001600160a01b03166001600160a01b03199190911617600355005b346102d65760203660031901126102d6576004356111e581610493565b6111ed612306565b6001600160a01b036112028282161515611f08565b6009541690813b156102d657604051632a47b83760e21b81526001600160a01b0382166004820152915f908390602490829084905af1908115610383577f278c70ced5f3e0e5eeb385b5ff9cb735748ba00a625147e66065ed48fc1562cd92610a9992611285575b506040516001600160a01b0390911681529081906020820190565b8061037d611292926105df565b5f61126a565b346102d6575f3660031901126102d6576004546040516001600160a01b039091168152602090f35b346102d65760203660031901126102d6576004356112dd81610493565b6112e5612306565b6001600160a01b03166112f9811515611f08565b7fc847328f5f08e3e6d7560b2fe64b4d448c99e6b7069d5bb25daa0dc4b9aba0e46020604051838152a16001600160601b0360a01b60095416176009555f80f35b346102d65760203660031901126102d6577f0816fa01d8b9b2a738d759950319611d0d7362176e5046cc25e2fef245f40f62602060043561137a81610493565b611382612306565b6001600160a01b0316611396811515611f08565b600680546001600160a01b03191682179055604051908152a1005b346102d6575f3660031901126102d6576020600254604051908152f35b346102d6575f3660031901126102d6576003546113f160ff8260a81c16156119e6565b6001546001600160a01b039160ff91831633148015611568575b61141490611aba565b60a01c161561141f57005b61142761228f565b611506575b6040516370a0823160e01b815230600482015290602090829060249082907f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b165afa908115610383575f916114e7575b508061148457005b600554611499906001600160a01b0316610324565b803b156102d65760405163e2ab691d60e01b815230600482015260248101929092525f60448301819052908290606490829084905af180156103835715610016578061037d610016926105df565b611500915060203d60201161059f57610590818361062d565b5f61147c565b60055461151b906001600160a01b0316610324565b803b156102d65760405163312ff83960e01b815260016004820152905f908290602490829084905af1801561038357611555575b5061142c565b8061037d611562926105df565b5f61154f565b50335f908152600b6020526040902054821661140b565b346102d65760203660031901126102d6577f5fce0723deeb6bf17731a5014d16c200d595ce4be09aab825145ca070cd35bf860206004356115bf81610493565b6115c7612306565b6001600160a01b03166115db811515611f08565b600880546001600160a01b03191682179055604051908152a1005b346102d65760203660031901126102d65760043561161381610493565b60018060a01b03165f52600b602052602060ff60405f2054166040519015158152f35b346102d6575f3660031901126102d6576008546040516001600160a01b039091168152602090f35b346102d65760203660031901126102d6577f4c0dc15273d1f148c7ea4bec6677928a493bbadcff873a85943f2c21b8186219602060043561169e81610493565b6116a6612306565b6001600160a01b03166116ba811515611f08565b600480546001600160a01b03191682179055604051908152a1005b346102d6575f806003193601126102d6576116ee612306565b6040517ffc6c5ac637f59dfbd7866ad96839e5f10712c8ee8f74ac0a28b2126ba34ffa2b5f80a1600854600254906001600160a01b0316803b156102d6576024835f8193819563785f6df160e11b845260048401525af1801561038357611753575080f35b61001691506105df565b346102d65760203660031901126102d65760043561177a81610493565b611782612306565b6001600160a01b039081169081156117d057600154826001600160601b0360a01b821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b346102d6575f3660031901126102d6576003546040516001600160a01b039091168152602090f35b346102d6575f3660031901126102d657611864612306565b6009546001600160a01b031661187b811515611f08565b604051907f89e2b1447ef7d47afaffbb3e1f1720cae5372df517f9992eed2528a8649b0c3e5f80a1803b156102d657816004815f8094634b687eed60e11b83525af18015610383576118c957005b610016906105df565b156118d957565b60405162461bcd60e51b815260206004820152601660248201527510dbdb9d1c9858dd081a5cc81b9bdd081c185d5cd95960521b6044820152606490fd5b6040513d5f823e3d90fd5b908160209103126102d6575161193781610445565b90565b8290602092606460018060a01b03600a54169260405196879586948593631b594def60e31b8552600485015260406024850152816044850152848401375f828201840152601f01601f191681010301915afa908115610383575f916119b7575b50156119ab57630b135d3f60e11b90565b6001600160e01b031990565b6119d9915060203d6020116119df575b6119d1818361062d565b810190611922565b5f61199a565b503d6119c7565b156119ed57565b60405162461bcd60e51b815260206004820152601260248201527110dbdb9d1c9858dd081a5cc81c185d5cd95960721b6044820152606490fd5b611a3960ff60035460a81c16156119e6565b611a5b60018060a01b0380928160015416331480156105b3576104f690611aba565b600c54611a7290610324906001600160a01b031681565b8114611aad575b6040516370a0823160e01b815230600482015290602082602481845afa9081156103835761065b925f9261057557506123a7565b611ab561235e565b611a79565b15611ac157565b60405162461bcd60e51b81526020600482015260166024820152751cd95b99195c881b9bdd081dda1a5d195b1a5cdd195960521b6044820152606490fd5b15611b0657565b60405162461bcd60e51b8152602060048201526015602482015274086c2dc40dcdee840e6eecacae040c2d8d84086acb605b1b6044820152606490fd5b908160209103126102d6575190565b9080611b5c612306565b611bb4575b81611b8f7f4969e7d59c970ec13a80e7e632b460c9202863355217c5d7ee7d0b5d7782e7c793511515611c59565b611ba0611b9b82611ca5565b600255565b611baf60405192839283611cc7565b0390a1565b611bbc612306565b604051917ffc6c5ac637f59dfbd7866ad96839e5f10712c8ee8f74ac0a28b2126ba34ffa2b5f80a1600854600254906001600160a01b0316803b156102d6576024855f8193819563785f6df160e11b845260048401525af1928315610383577f4969e7d59c970ec13a80e7e632b460c9202863355217c5d7ee7d0b5d7782e7c793611c4a575b509150611b61565b611c53906105df565b5f611c42565b15611c6057565b60405162461bcd60e51b815260206004820152601860248201527f6d757374206e6f7420626520656d70747920737472696e6700000000000000006044820152606490fd5b602081519101519060208110611cb9575090565b5f199060200360031b1b1690565b90606060208092959495604085528051918291826040880152018386015e5f828286010152601f8019910116830101931515910152565b6001600160a01b03918216815291166020820152604081019190915260806060820181905281018390526001600160fb1b0383116102d65760a09260051b809284830137010190565b91908203918211611d5457565b634e487b7160e01b5f52601160045260245ffd5b6040516370a0823160e01b8082523060048301526001600160a01b03848116979096949560209593949186866024818d5afa95861561038357879389915f98611ee7575b506004545f9190611de790611dc9906001600160a01b0316610324565b94604051998a9788968795637d5f6a0960e11b875260048701611cfe565b03925af1918215610383578492611eca575b5060405190815230600482015295869060249082905afa80156103835761065b95611e2c935f92611ead575b5050611d47565b604080514281526001600160a01b03841660208201529081018290527f3dd299e38bc729d3874c3f50136afc29585dfdaec4766d1e1231d0e56da9672690606090a180611e9d575b50507f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e16611a27565b611ea6916123a7565b5f80611e74565b611ec39250803d1061059f57610590818361062d565b5f80611e25565b611ee090833d851161059f57610590818361062d565b505f611df9565b5f919850611f0190863d881161059f57610590818361062d565b9790611dac565b15611f0f57565b60405162461bcd60e51b815260206004820152601860248201527f6d757374206e6f74206265207a65726f206164647265737300000000000000006044820152606490fd5b634e487b7160e01b5f52603260045260245ffd5b9190811015611f8a5760051b81013590607e19813603018212156102d6570190565b611f54565b3561193781610493565b903590601e19813603018212156102d657018035906001600160401b0382116102d657602001918160051b360383136102d657565b6001600160401b0381116105f25760051b60200190565b9291611ff082611fce565b91611ffe604051938461062d565b829481845260208094019160051b81019283116102d657905b8282106120245750505050565b81358152908301908301612017565b1561203a57565b60405162461bcd60e51b815260206004820152601f60248201527f726577617264206d757374206e6f74206265207a65726f2061646472657373006044820152606490fd5b6020929460c09260a083019560018060a01b038092168452859786850152166040830152606082015260a060808201528551809452019301915f5b8281106120c8575050505090565b8351855293810193928101926001016120ba565b5f5b82811061211b575061065b9150507f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e6001600160a01b0316611a27565b61212e612129828585611f68565b611f8f565b90602061213c828686611f68565b01359160408061214d848888611f68565b01359061217261216b612161868a8a611f68565b6060810190611f99565b3691611fe5565b6121866001600160a01b0385161515612033565b60075461219b906001600160a01b0316610324565b803b156102d6578251630968c76b60e11b8152915f9183918290849082906121ca908a308f8e6004870161207f565b03925af19586156103835761222c7fc8fb114192ae3823bff69d1b8922f93f9e88da0ae61a2145277ca71bd550773b93859360019961224d575b50516001600160a01b0387168152602081019290925260408201929092529081906060820190565b0390a18061223d575b5050016120de565b612246916123a7565b5f80612235565b8061037d61225a926105df565b5f612204565b3d1561228a573d906122718261065d565b9161227f604051938461062d565b82523d5f602084013e565b606090565b5f8060405160208101630241d3fb60e11b8152306024830152602482526122b582610612565b6005549151916001600160a01b03165afa6122ce612260565b901580156122e7575b6122e2576040015190565b505f90565b5060408151106122d7565b8051821015611f8a5760209160051b010190565b6001546001600160a01b0316330361231a57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b47806123675750565b600c546001600160a01b0316803b156102d6575f90600460405180948193630d0e30db60e41b83525af180156103835761239e5750565b61065b906105df565b6001600160a01b038181167f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e82161461246457506006546123fc9083906123f6906001600160a01b0316610324565b83612651565b600654612411906001600160a01b0316610324565b91823b156102d657604051631f72642160e31b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015610383576124575750565b8061037d61065b926105df565b604051636e553f6560e01b81525f19600482015230602482015292507f0000000000000000000000000655977feb2f289a4ab78af67bab0d17aab843671690506020826044815f855af19182156103835761065b926124c4575b50611a27565b6124dc9060203d60201161059f57610590818361062d565b505f6124be565b60025f54146124f25760025f55565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b60405163095ea7b360e01b602082019081526001600160a01b0390931660248201525f1960448201525f9283929183906125748160648101610d7c565b51925af1612580612260565b816125e5575b501561258e57565b60405162461bcd60e51b815260206004820152602960248201527f455243323048656c706572733a3a73616665417070726f76653a20617070726f6044820152681d994819985a5b195960ba1b6064820152608490fd5b80518015925082156125fa575b50505f612586565b61260d9250602080918301019101611922565b5f806125f2565b60405163095ea7b360e01b602082019081526001600160a01b0390931660248201525f604482018190529283929183906125748160648101610d7c565b60405163095ea7b360e01b602082019081526001600160a01b03909316602482015260448101939093525f9283929083906125748160648101610d7c565b9060209182818303126102d6578051906001600160401b0382116102d6570181601f820112156102d6578051926126c584611fce565b936040936126d6604051968761062d565b818652828087019260061b850101938185116102d6578301915b8483106127005750505050505090565b85838303126102d6578386918251612717816105f7565b855161272281610493565b815282860151838201528152019201916126f0565b9061274182611fce565b6040612750604051928361062d565b8382528193612761601f1991611fce565b01905f5b8281106127725750505050565b81519060608201918083106001600160401b038411176105f25760209284525f8152825f818301525f85830152828701015201612765565b6005546127bf906001600160a01b0316610324565b6040805163dc01f60d60e01b8152306004808301919091529093925f90859060249082905afa938415610383575f946128d8575b506127fe8451612737565b915f5b85518110156128d157612817610f4282886122f2565b9060209182612826838a6122f2565b5101519261283d610324610324610f42868d6122f2565b85516370a0823160e01b81523088820190815290959183918791908290819060200103915afa918215610383576001955f936128b2575b5061288f61288061064e565b6001600160a01b039095168552565b830152848201526128a082876122f2565b526128ab81866122f2565b5001612801565b816128ca9294503d851161059f57610590818361062d565b915f612874565b5050509150565b6128f59194503d805f833e6128ed818361062d565b81019061268f565b925f6127f356fea2646970667358221220ec785b8d30a2ebdee2e8276dc7b236de6535ad1aa8ba2e2d3f02100f1aa5c2ee64736f6c63430008190033

Verified Source Code Full Match

Compiler: v0.8.25+commit.b61c2a91 EVM: cancun Optimization: Yes (200 runs)
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.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.
 *
 * By default, the owner account will be the one that deploys the contract. 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 Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @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) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IERC20Metadata.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/extensions/IERC20Metadata.sol";
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
Strings.sol 85 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}
ECDSA.sol 217 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                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.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && 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 down.
     *
     * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}
IUniswapV2Pair.sol 52 lines
pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}
Manager.sol 482 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {IVotiumMultiMerkleStash} from "./interfaces/IVotiumMultiMerkleStash.sol";
import {ICvxDelegateRegistry} from "./interfaces/ICvxDelegateRegistry.sol";
import {ICvxLocker} from "./interfaces/ICvxLocker.sol";

import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IRegistryDelegate.sol";
import "./interfaces/IFeeSplitter.sol";
import "./libraries/ERC20Helpers.sol";
import "./interfaces/IVoteMarket.sol";
import "./interfaces/IDeposit.sol";
import "./SignatureVerifier.sol";
import "./interfaces/IWETH.sol";

/**
 * @title Manager
 * @notice Contract that manages locked cvx
 */
contract Manager is ReentrancyGuard, Ownable {

  event SetVotium(address votium);
  event SetVoteMarket(address voteMarket);
  event SetFeeSplitter(address feeSplitter);

  event SetCvxLocker(address locker);
  event SetCvxDelegate(address delegate);

  event SetDelegationSpace(string _delegationSpace, bool shouldClear);
  event SetVoteDelegate(address voteDelegate);
  event ClearVoteDelegate();

  event ClaimVoteMarketRewards(uint256 timestamp, address reward, uint256 amount);
  event ClaimConvexRewards(uint256 timestamp, address reward, uint256 amount);
  event ClaimVotiumReward(address token, uint256 index, uint256 amount);

  event SetRegistryDelegate(address _registryDelegate);
  event SetRegistry(address registryDelegate);
  event RegistryCleared();

  bytes32 public delegationSpace = bytes32("cvx.eth");
  IDeposit public immutable scrvUSD;
  address public emergencyMigrator;
  IERC20 public immutable crvUSD;
  IERC20 public immutable CVX;
  bool public relockPaused;
  bool public paused;

  IVoteMarket public voteMarket;
  ICvxLocker public cvxLocker;
  IFeeSplitter public feeSplitter;
  IVotiumMultiMerkleStash public votium;
  ICvxDelegateRegistry public cvxDelegate;
  IRegistryDelegate public registryDelegate;

  SignatureVerifier public verifier;

  mapping(address => bool) public whitelisted;

  IWETH weth = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

  /**
    @notice Convex reward details
    @param  token    address  Token
    @param  amount   uint256  Amount
    @param  balance  uint256  Balance (used for calculating the actual received amount)
   */
  struct ConvexReward {
    address token;
    uint256 amount;
    uint256 balance;
  }

  /**
    @param  _CVX          address  CVX address
    @param  _feeSplitter  address  FeeSplitter address
    @param  _cvxLocker    address  CvxLocker address
    @param  _cvxDelegate  address  CvxDelegateRegistry address
    @param  _votium       address  VotiumMultiMerkleStash address
    @param  _voteMarket     address  VoteMarket address
   */
  constructor(
    address _CVX,
    address _crvUSD,
    address _scrvUSD,
    address _feeSplitter,
    address _cvxLocker,
    address _cvxDelegate,
    address _votium,
    address _voteMarket,
    address _verifier
  ) {
    cvxDelegate = ICvxDelegateRegistry(_cvxDelegate);
    votium = IVotiumMultiMerkleStash(_votium);
    feeSplitter = IFeeSplitter(_feeSplitter);
    verifier = SignatureVerifier(_verifier);
    voteMarket = IVoteMarket(_voteMarket);
    cvxLocker = ICvxLocker(_cvxLocker);
    scrvUSD = IDeposit(_scrvUSD);
    crvUSD = IERC20(_crvUSD);
    CVX = IERC20(_CVX);

    crvUSD.approve(_scrvUSD, type(uint256).max);
    CVX.approve(_cvxLocker, type(uint256).max);
  }

  /**
   * @notice Sets whitelisted status of an address
   * @param user address user to set
   * @param _whitelisted bool whether or not the user should become whitelisted
   */
  function setWhitelisted(address user, bool _whitelisted) external onlyOwner {
    whitelisted[user] = _whitelisted;
  }

  /**
   * @notice withdraws the specified token to the msg sender. Can only do when paused
   * @param _token address The token to withdraw
   */
  function withdrawToken(address _token) external nonReentrant whenPaused onlyOwner {
    IERC20 token = IERC20(_token);
    uint256 amount = token.balanceOf(address(this));
    ERC20Helpers.safeTransfer(_token, msg.sender, amount);
  }

  /**
   * @notice sweeps the specified token to the fee distributor
   * @param _token address The token to sweep
   */
  function sweepToken(address _token) public whenNotPaused isWhitelisted {
    require(_token != address(CVX), "Can not sweep all CVX");
    if (_token == address(weth)) wrapEth();

    IERC20 token = IERC20(_token);
    uint256 amount = token.balanceOf(address(this));
    _sweepToken(address(token), amount);
  }

  /**
   * @notice Internal function that wraps eth into WETH
   */
  function wrapEth() internal {
    uint256 balance = address(this).balance;
    if (balance > 0) weth.deposit{ value: balance }();
  }

  /**
   * @notice Internal function that sweeps specific amounts of tokens
   */
  function _sweepToken(address _token, uint256 amount) internal {
    if (_token == address(crvUSD)) return _wrapAndSweepCrvUsd();
    ERC20Helpers.safeApprove(_token, address(feeSplitter), amount);
    feeSplitter.distribute(_token, amount);
  }

  /**
   * @notice wraps crvUSD in scrvUSD and sweeps to the fee distributor
   */
  function _wrapAndSweepCrvUsd() internal {
    scrvUSD.deposit(type(uint256).max, address(this));
    sweepToken(address(scrvUSD));
  }

  /**
   * @notice Get claimable rewards and balances
   * @return rewards  ConvexReward[]  Claimable rewards and balances
   */
  function _claimableConvexRewards() internal view
  returns (ConvexReward[] memory rewards) {
    // Get claimable rewards
    ICvxLocker.EarnedData[] memory earnings = cvxLocker.claimableRewards(address(this));
    rewards = new ConvexReward[](earnings.length);

    // Get the current balances for each token to calculate the amount received
    for (uint256 i; i < earnings.length; ++i)
      rewards[i] = ConvexReward({
        token: earnings[i].token,
        amount: earnings[i].amount,
        balance: IERC20(earnings[i].token).balanceOf(address(this))
      });
  }

  /**
   * @notice Claim rewards from convex (e.g. emissions) and distribute
   */
  function claimConvexRewards() external nonReentrant isWhitelisted {
    ConvexReward[] memory rewards = _claimableConvexRewards();

    // claim rewards
    cvxLocker.getReward(address(this), false);

    // Iterate over rewards and distribute
    for (uint256 i; i < rewards.length; ++i) {
      emit ClaimConvexRewards(block.timestamp, rewards[i].token, rewards[i].amount);
      if (rewards[i].amount > 0) _sweepToken(rewards[i].token, rewards[i].amount);
    } sweepToken(address(crvUSD));
  }

  /**
   * @notice Claim multiple VoteMarket rewards
   * @param  account  address account for which to claim
   * @param  reward  address token address of reward to claim
   * @param  claimable  uint256 amount of token to claim
   * @param  proof  bytes32[] claim merkle proof
   */
  function claimVoteMarketRewards(
    address account, address reward, uint256 claimable, bytes32[] calldata proof
  ) external whenNotPaused nonReentrant isWhitelisted {
    uint256 balanceWas = IERC20(reward).balanceOf(address(this));

    voteMarket.claim(account, reward, claimable, proof);
    claimable = IERC20(reward).balanceOf(address(this)) - balanceWas;

    emit ClaimVoteMarketRewards(block.timestamp, reward, claimable);

    if (claimable > 0) _sweepToken(reward, claimable);
    sweepToken(address(crvUSD));
  }

  /**
   * @notice Claim multiple Votium rewards
   * @param  votiumRewards  VotiumRewards[]  Votium rewards metadata
  */
  function claimVotiumRewards(
    IVotiumMultiMerkleStash.claimParam[] calldata votiumRewards
  ) external whenNotPaused nonReentrant isWhitelisted {
    for (uint256 i; i < votiumRewards.length; ++i) {
      address token = votiumRewards[i].token;
      uint256 index = votiumRewards[i].index;
      uint256 amount = votiumRewards[i].amount;
      bytes32[] memory merkleProof = votiumRewards[i].merkleProof;

      require(token != address(0), "reward must not be zero address");

      // Validates `token`, `index`, `amount`, and `merkleProof`
      votium.claim(
        token, index, address(this),
        amount, merkleProof
      );
      emit ClaimVotiumReward(token, index, amount);
      if (amount > 0) _sweepToken(token, amount);
    } sweepToken(address(crvUSD));
  }

  /**
   * @notice Set delegationSpace
   * @param  _delegationSpace  string  Convex Snapshot delegation space
   * @param  shouldClear       bool    Whether to clear the vote delegate for current delegation space
   */
  function setDelegationSpace(
    string memory _delegationSpace,
    bool shouldClear
  ) external onlyOwner {
    if (shouldClear) clearVoteDelegate();

    bytes memory space = bytes(_delegationSpace);
    require(space.length != 0, "must not be empty string");

    delegationSpace = bytes32(space);
    emit SetDelegationSpace(_delegationSpace, shouldClear);
  }

  /**
   * @notice Set vote delegate
   * @param  voteDelegate  address  Account to delegate votes to
   */
  function setVoteDelegate(address voteDelegate) external onlyOwner {
    require(voteDelegate != address(0), "must not be zero address");

    emit SetVoteDelegate(voteDelegate);

    cvxDelegate.setDelegate(delegationSpace, voteDelegate);
  }

  /**
   * @notice sets registry delegate
   * @param _registryDelegate address of delegate
   */
  function setRegistryDelegate(address _registryDelegate) external onlyOwner {
    require(_registryDelegate != address(0), "must not be zero address");

    emit SetRegistryDelegate(_registryDelegate);
    registryDelegate = IRegistryDelegate(_registryDelegate);
  }

  /**
   * @notice clears registry
   */
  function clearRegistry() external onlyOwner {
    require(address(registryDelegate) != address(0), "must not be zero address");

    emit RegistryCleared();
    registryDelegate.setToExpire();
  }

  /**
   * @notice Sets registry
   * @param  registry  address  Account to receive rewards from votium
   */
  function setRegistry(address registry) external onlyOwner {
    require(registry != address(0), "must not be zero address");

    registryDelegate.setRegistry(registry);
    emit SetRegistry(registry);
  }

  /**
   * @notice Remove vote delegate
   */
  function clearVoteDelegate() public onlyOwner {
    emit ClearVoteDelegate();

    cvxDelegate.clearDelegate(delegationSpace);
  }

  /**
   * @notice Set feeSplitter address
   * @param  _feeSplitter address _feeSplitter address
   */
  function setFeeSplitter(address _feeSplitter) external onlyOwner {
    require(_feeSplitter != address(0), "must not be zero address");

    feeSplitter = IFeeSplitter(_feeSplitter);
    emit SetFeeSplitter(_feeSplitter);
  }

  /**
   * @notice Set cvx locker address
   * @param  locker  address  Locker address
   */
  function setCvxLocker(address locker) external onlyOwner {
    require(locker != address(0), "must not be zero address");
    require(locker != address(cvxLocker), "Already set");

    ERC20Helpers.safeApprove(address(CVX), address(locker), type(uint256).max);
    ERC20Helpers.safeApprove(address(CVX), address(cvxLocker), 0);
    cvxLocker = ICvxLocker(locker);
    emit SetCvxLocker(locker);
  }

  /**
   * @notice Set votium
   * @param  _votium  address  votium address
   */
  function setVotium(address _votium) external onlyOwner {
    require(_votium != address(0), "must not be zero address");

    votium = IVotiumMultiMerkleStash(_votium);
    emit SetVotium(_votium);
  }

  /**
   * @notice Set voteMarket
   * @param  _voteMarket  address  voteMarket address
   */
  function setVoteMarket(address _voteMarket) external onlyOwner {
    require(_voteMarket != address(0), "must not be zero address");

    voteMarket = IVoteMarket(_voteMarket);
    emit SetVoteMarket(_voteMarket);
  }

  /// @notice Sets the Convex delegate registry address for managing vote delegation.
  /// @param delegate The address of the `ICvxDelegateRegistry` to use for delegations.
  function setCvxDelegate(address delegate) external onlyOwner {
    require(delegate != address(0), "must not be zero address");
    cvxDelegate = ICvxDelegateRegistry(delegate);
    emit SetCvxDelegate(delegate);
  }

  /**
   * @notice Relocks cvx in cvxLocker,
   *   or cvx that has been kicked to this contract
   */
  function relock() public whenNotPaused isWhitelisted {
    if (!relockPaused) {
      (uint256 unlockable) = _fetchUnlockable();
      if (unlockable > 0) cvxLocker.processExpiredLocks(true);

      uint256 cvxBalance = CVX.balanceOf(address(this));
      if (cvxBalance > 0) cvxLocker.lock(address(this), cvxBalance, 0);
    }
  }

  // Due to a strange bug involving unbounded arrays,
  // we use this safe version of accessing unlockable
  // amounts from the cvxLocker.
  function _fetchUnlockable() public view returns (uint256 unlockable) {
    // build the calldata for lockedBalances(address)
    bytes memory data = abi.encodeWithSelector(
      ICvxLocker.lockedBalances.selector,
      address(this)
    );

    // staticcall so any internal revert just gives ok=false
    (bool ok, bytes memory ret) = address(cvxLocker).staticcall(data);

    // if the call reverted or returned < 2 words, bail with 0
    if (!ok || ret.length < 64) return 0;

    // otherwise the 2nd returned word is at offset 32 (length) + 32 (total) = 64
    assembly {
      unlockable := mload(add(ret, 64))
    }
  }

  modifier isWhitelisted() {
    require(msg.sender == owner() || whitelisted[msg.sender], "sender not whitelisted");
    _;
  }

  /*//////////////////////////////////////////////////////////////
    EMERGENCY/MIGRATION LOGIC
  //////////////////////////////////////////////////////////////*/

  modifier whenPaused() {
    require(paused, "Contract is not paused");
    _;
  }

  modifier whenNotPaused() {
    require(!paused, "Contract is paused");
    _;
  }

  /**
   * @notice Set the contract's relockPaused state
   * @param state relockPaused state
   */
  function setRelockPaused(bool state) external onlyOwner {
    relockPaused = state;
  }

  /**
   * @notice Set the contract's pause state
   * @param state Pause state
   */
  function setPaused(bool state) external onlyOwner {
    paused = state;
  }

  /**
   * @notice Sets the emergency migrator, but only when paused
   */
  function setEmergencyMigrator(address _migrator) external whenPaused onlyOwner {
    emergencyMigrator = _migrator;
  }

  /**
   * @notice Emergency withdraw to the emergency Migrator
   */
  function triggerEmergencyMigration() external whenPaused onlyOwner {
    require(emergencyMigrator != address(0), "emergency migrator not set");
    cvxLocker.withdrawExpiredLocksTo(emergencyMigrator);
  }

  /// @notice Updates the on-chain signature verifier contract.
  /// @param _auctioneer The address of the new `SignatureVerifier` instance.
  function updateVerifier(address _auctioneer) public onlyOwner {
    verifier = SignatureVerifier(_auctioneer);
  }

  /// @notice Implements ERC-1271: validates a signature against the approved team.
  /// @param _hash      The hash of the data signed.
  /// @param _signature The signature to validate.
  /// @return magicValue Returns `0x1626ba7e` if valid, else `0xffffffff` as per ERC-1271.
  function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4) {
    if (verifier.verifySignature(_hash, _signature))
      return 0x1626ba7e;
    else return 0xffffffff;
  }

  /// @notice Receive native ETH
  receive() external payable { }

  /// @notice Fallback to receive native ETH
  fallback() external payable { }

}
SignatureVerifier.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract SignatureVerifier is Ownable {
  mapping(address => bool) public approvedTeam;

  constructor() {
    approvedTeam[msg.sender] = true;
  }

  /// @notice Approve or revoke a team member for signature verification.
  /// @param _member The address of the team member to update.
  /// @param _approval True to approve the member; false to revoke approval.
  function modifyTeam(address _member, bool _approval) public onlyOwner {
    approvedTeam[_member] = _approval;
  }

  /// @notice Checks whether a signature over a given hash was made by an approved team member.
  /// @param _hash   The hash of the signed message.
  /// @param _signature The signature bytes.
  /// @return True if the recovered signer is in the approved team; otherwise false.
  function verifySignature(bytes32 _hash, bytes memory _signature) public view returns (bool) {
    address signer = ECDSA.recover(_hash, _signature);
    return approvedTeam[signer];
  }
}
ICvxDelegateRegistry.sol 10 lines
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.25;

interface ICvxDelegateRegistry {
  function setDelegate(bytes32 id, address delegate) external;

  function clearDelegate(bytes32 id) external;

  function delegation(address account, bytes32 id) external view returns (address);
}
ICvxLocker.sol 52 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface ICvxLocker {
  struct LockedBalance {
    uint112 amount;
    uint112 boosted;
    uint32 unlockTime;
  }
  struct EarnedData {
    address token;
    uint256 amount;
  }

  struct Balances {
    uint112 locked;
    uint112 boosted;
    uint32 nextUnlockIndex;
  }

  function lock(
    address _account,
    uint256 _amount,
    uint256 _spendRatio
  ) external;

  function userLocks(address _user, uint256 index) external view returns (LockedBalance memory);
  function balances(address _user) external view returns (Balances memory);

  function lockedBalances(address _user)
  external
  view
  returns (
    uint256 total,
    uint256 unlockable,
    uint256 locked,
    LockedBalance[] memory lockData
  );

  function withdrawExpiredLocksTo(address recipient) external;
  function processExpiredLocks(bool _relock) external;

  function claimableRewards(address _account)
  external
  view
  returns (EarnedData[] memory userRewards);

  function getReward(address _account, bool _stake) external;

  function lockedBalanceOf(address _user) external view returns (uint256 amount);
  function balanceOf(address _user) external view returns (uint256 amount);
}
IDeposit.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

interface IDeposit {
  function balanceOf(address account) external view returns (uint256);
  function previewRedeem(uint256 amount) external view returns (uint256);
  function previewWithdraw(uint256 amount) external view returns (uint256);
  function approve(address spender, uint256 amount) external returns (bool);
  function deposit(uint256 amount, address receiver) external returns (uint256);
  function withdraw(uint256 amount, address receiver, address owner) external returns (uint256);
  function redeem(uint256 amount, address receiver, address owner) external returns (uint256);
}


IFeeSplitter.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IFeeSplitter {
	function requestRewards() external returns (uint256);
	function distribute(address token, uint256 amount) external;
}
IRegistryDelegate.sol 15 lines
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.25;

interface IRegistryDelegate {
  function setRegistry(address registry) external;
  function setToExpire() external;
  struct Registry {
    uint256 start;      // when first registering, there is a delay until the next vlCVX voting epoch starts
    address to;         // forward rewards to alternate address OR 0x0 address for OPT OUT of rewards
    uint256 expiration; // when ending an active registration, expiration is set to the next vlCVX voting epoch
                        // an active registration cannot be changed until after it is expired (one vote round delay when changing active registration)
  }

  function registry(address from) external view returns (Registry memory);
}
IVoteMarket.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IVoteMarket {

  function claim(
    address account,
    address reward,
    uint256 claimable,
    bytes32[] calldata proof
  ) external returns (uint256 amount);

}

IVotiumMultiMerkleStash.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IVotiumMultiMerkleStash {
  struct claimParam {
    address token;
    uint256 index;
    uint256 amount;
    bytes32[] merkleProof;
  }

  function claim(
    address token,
    uint256 index,
    address account,
    uint256 amount,
    bytes32[] calldata merkleProof
  ) external;
}
IWETH.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

interface IWETH {
  function deposit() external payable;
}
ERC20Helpers.sol 58 lines
// SPDX-License-Identifier: GPL-3.0-or-later
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';

pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library ERC20Helpers {

  function safeApprove(
    address token,
    address to,
    uint256 value
  ) internal {
    // bytes4(keccak256(bytes('approve(address,uint256)')));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'ERC20Helpers::safeApprove: approve failed'
    );
  }

  function safeTransfer(
    address token,
    address to,
    uint256 value
  ) internal {
    // bytes4(keccak256(bytes('transfer(address,uint256)')));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'ERC20Helpers::safeTransfer: transfer failed'
    );
  }

  function safeTransferFrom(
    address token,
    address from,
    address to,
    uint256 value
  ) internal {
    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'ERC20Helpers::transferFrom: transferFrom failed'
    );
  }

  function safeTransferETH(address to, uint256 value) internal {
    (bool success, ) = to.call{value: value}(new bytes(0));
    require(success, 'ERC20Helpers::safeTransferETH: ETH transfer failed');
  }

}



Read Contract

CVX 0x759cb53b → address
_fetchUnlockable 0x63f2615d → uint256
crvUSD 0x9bec8288 → address
cvxDelegate 0xe6a17b9a → address
cvxLocker 0x8d68f9ff → address
delegationSpace 0xbbea0974 → bytes32
emergencyMigrator 0xf9216f11 → address
feeSplitter 0x6052970c → address
isValidSignature 0x1626ba7e → bytes4
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
registryDelegate 0x92f25ea0 → address
relockPaused 0x64521070 → bool
scrvUSD 0x858a43d1 → address
verifier 0x2b7ac3f3 → address
voteMarket 0xa98a9304 → address
votium 0x2a5b5d13 → address
whitelisted 0xd936547e → bool

Write Contract 25 functions

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

claimConvexRewards 0x8cedddb5
No parameters
claimVoteMarketRewards 0x260ee14d
address account
address reward
uint256 claimable
bytes32[] proof
claimVotiumRewards 0x3532b32c
tuple[] votiumRewards
clearRegistry 0xfc6530c1
No parameters
clearVoteDelegate 0xedf187f0
No parameters
relock 0xc53b573d
No parameters
renounceOwnership 0x715018a6
No parameters
setCvxDelegate 0xce62d8eb
address delegate
setCvxLocker 0x5f477c57
address locker
setDelegationSpace 0x1d3c2b2f
string _delegationSpace
bool shouldClear
setEmergencyMigrator 0xa6785348
address _migrator
setFeeSplitter 0xb17fb9d6
address _feeSplitter
setPaused 0x16c38b3c
bool state
setRegistry 0xa91ee0dc
address registry
setRegistryDelegate 0xabc0ab51
address _registryDelegate
setRelockPaused 0x5834d435
bool state
setVoteDelegate 0x74874323
address voteDelegate
setVoteMarket 0xeb2578ef
address _voteMarket
setVotium 0x3a16b85a
address _votium
setWhitelisted 0x9281aa0b
address user
bool _whitelisted
sweepToken 0x1be19560
address _token
transferOwnership 0xf2fde38b
address newOwner
triggerEmergencyMigration 0x0bc8e05f
No parameters
updateVerifier 0x97fc007c
address _auctioneer
withdrawToken 0x89476069
address _token

Recent Transactions

No transactions found for this address