Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xC0297a0E39031F09406F0987C9D9D41c5dfbc3df
Balance 0 ETH
Nonce 1
Code Size 23343 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23343 bytes
0x608060405260043610610424575f3560e01c80638a5fddd811610220578063c33f59d311610129578063d5a10065116100b3578063e46cf74711610078578063e46cf74714610e45578063ef8b30f714610e64578063f548833014610e83578063f7d3180914610e98578063ff3368a114610eb7575f80fd5b8063d5a1006514610da7578063da40385d14610dbb578063db91323614610dda578063dd62ed3e14610df9578063ddca3f4314610e18575f80fd5b8063cdf456e1116100f9578063cdf456e114610cee578063ce318c5114610d21578063cf1de5d814610d40578063d04bf02414610d55578063d21220a714610d74575f80fd5b8063c33f59d314610c7b578063c415b95c14610c9c578063c6e6f59214610cbb578063c879657214610cda575f80fd5b8063a9059cbb116101aa578063af38d7571161017a578063af38d75714610bf5578063b3ab15fb14610c09578063b3ddda2a14610c28578063b6b55f2514610c47578063bba9282e14610c66575f80fd5b8063a9059cbb14610b84578063aa2f892d14610ba3578063ab710b2414610bc2578063abaa991614610be1575f80fd5b8063953d7ee2116101f0578063953d7ee214610aea57806395d89b4114610b1d57806395f9e9e614610b31578063a42dce8014610b50578063a4c84f2514610b6f575f80fd5b80638a5fddd8146109eb5780638da5cb5b14610a00578063937b258114610a1457806394db059514610ad6575f80fd5b806331ca1c021161032d578063570ca735116102b75780636e553f651161027c5780636e553f65146109235780636ec686251461094257806370a08231146109615780637d8187f9146109a15780638803dbee146109cc575f80fd5b8063570ca73514610889578063674eb980146108a757806369fe0e2d146108c65780636c08c57e146108e55780636d785a8714610904575f80fd5b80633fc8cef3116102fd5780633fc8cef3146107bc57806345059a6b146107ef5780634a8ff603146108045780634cdad5061461083757806350d0ea3914610856575f80fd5b806331ca1c021461070b57806335ce81c41461072057806338d52e0f1461075e57806338ed173914610790575f80fd5b806313af4035116103ae578063209b2bca1161037e578063209b2bca1461064d57806323b872dd146106805780632eb6328b1461069f57806330486f3c146106d1578063313ce567146106f0575f80fd5b806313af40351461059a57806318160ddd146105b95780631c8ec299146105ec57806320761fc41461061f575f80fd5b80630902f1ac116103f45780630902f1ac146104b7578063090b78c5146104e0578063095ea7b3146105015780630dfe1681146105305780630e608b301461057b575f80fd5b806301e1d1141461042f57806305fefda71461045657806306fdde031461047757806307a2d13a14610498575f80fd5b3661042b57005b5f80fd5b34801561043a575f80fd5b50610443610eea565b6040519081526020015b60405180910390f35b348015610461575f80fd5b50610475610470366004614ffa565b610f2d565b005b348015610482575f80fd5b5061048b61108b565b60405161044d919061501a565b3480156104a3575f80fd5b506104436104b2366004615066565b61114b565b3480156104c2575f80fd5b506104cb611196565b6040805192835260208301919091520161044d565b3480156104eb575f80fd5b506104436d629b8c891b267182b6140000000081565b34801561050c575f80fd5b5061052061051b366004615091565b61135f565b604051901515815260200161044d565b34801561053b575f80fd5b506105637f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b03909116815260200161044d565b348015610586575f80fd5b506104756105953660046150bb565b611376565b3480156105a5575f80fd5b506104756105b43660046150bb565b6113c7565b3480156105c4575f80fd5b507f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0254610443565b3480156105f7575f80fd5b506104437f000000000000000000000000000000000000000000000000000000000000025881565b34801561062a575f80fd5b506105206106393660046150bb565b603c6020525f908152604090205460ff1681565b348015610658575f80fd5b506105637f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561068b575f80fd5b5061052061069a3660046150d6565b6113db565b3480156106aa575f80fd5b506038546106be90620100009004600f0b81565b604051600f9190910b815260200161044d565b3480156106dc575f80fd5b506104756106eb366004615066565b6113fe565b3480156106fb575f80fd5b506040516012815260200161044d565b348015610716575f80fd5b5061044360645481565b34801561072b575f80fd5b5060355461074690600160801b90046001600160801b031681565b6040516001600160801b03909116815260200161044d565b348015610769575f80fd5b507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2610563565b34801561079b575f80fd5b506107af6107aa36600461515b565b6116b3565b60405161044d9190615202565b3480156107c7575f80fd5b506105637f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b3480156107fa575f80fd5b5061044360325481565b34801561080f575f80fd5b506104437f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b348015610842575f80fd5b50610443610851366004615066565b611829565b348015610861575f80fd5b506104437f000000000000000000000000000000000000000000000000000000000098968081565b348015610894575f80fd5b505f54610563906001600160a01b031681565b3480156108b2575f80fd5b506107af6108c1366004615214565b611833565b3480156108d1575f80fd5b506104756108e0366004615066565b6119f8565b3480156108f0575f80fd5b506107af6108ff366004615252565b611a09565b34801561090f575f80fd5b50603a54610563906001600160a01b031681565b34801561092e575f80fd5b5061044361093d3660046152ad565b611ad3565b34801561094d575f80fd5b50603554610746906001600160801b031681565b34801561096c575f80fd5b5061044361097b3660046150bb565b6001600160a01b03165f9081525f80516020615aba833981519152602052604090205490565b3480156109ac575f80fd5b506104436109bb366004615066565b60656020525f908152604090205481565b3480156109d7575f80fd5b506107af6109e636600461515b565b611ae5565b3480156109f6575f80fd5b5061044361271081565b348015610a0b575f80fd5b50610563611c3d565b348015610a1f575f80fd5b50610a87610a2e366004615066565b60376020525f90815260409020805460018201546002909201546001600160a01b03821692600160a01b830460ff1692600160a81b900464ffffffffff16916001600160801b0380831692600160801b90048116911686565b604080516001600160a01b039097168752941515602087015264ffffffffff909316938501939093526001600160801b03908116606085015291821660808401521660a082015260c00161044d565b348015610ae1575f80fd5b50610443611c58565b348015610af5575f80fd5b506105637f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b348015610b28575f80fd5b5061048b611c67565b348015610b3c575f80fd5b50610475610b4b366004615066565b611ca5565b348015610b5b575f80fd5b50610475610b6a3660046150bb565b611d85565b348015610b7a575f80fd5b50610443603d5481565b348015610b8f575f80fd5b50610520610b9e366004615091565b611d96565b348015610bae575f80fd5b506104cb610bbd366004615066565b611da3565b348015610bcd575f80fd5b50610475610bdc3660046150bb565b611fe7565b348015610bec575f80fd5b506104cb612228565b348015610c00575f80fd5b5061044361228c565b348015610c14575f80fd5b50610475610c233660046150bb565b6123bd565b348015610c33575f80fd5b50610475610c42366004615318565b6123ce565b348015610c52575f80fd5b50610443610c61366004615066565b612599565b348015610c71575f80fd5b5061044360365481565b348015610c86575f80fd5b506104436a0c097ce7bc90715b34b9f160241b81565b348015610ca7575f80fd5b50603954610563906001600160a01b031681565b348015610cc6575f80fd5b50610443610cd5366004615066565b6125a4565b348015610ce5575f80fd5b506104436125d4565b348015610cf9575f80fd5b506105637f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b348015610d2c575f80fd5b50603b54610563906001600160a01b031681565b348015610d4b575f80fd5b5061044360335481565b348015610d60575f80fd5b50610475610d6f3660046153c2565b6127f4565b348015610d7f575f80fd5b506105637f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b348015610db2575f80fd5b506104756129d8565b348015610dc6575f80fd5b50610475610dd5366004615214565b612dc1565b348015610de5575f80fd5b50610475610df43660046150bb565b612fe7565b348015610e04575f80fd5b50610443610e13366004615428565b61313c565b348015610e23575f80fd5b50603854610e329061ffff1681565b60405161ffff909116815260200161044d565b348015610e50575f80fd5b50610443610e5f366004615066565b613185565b348015610e6f575f80fd5b50610443610e7e366004615066565b613619565b348015610e8e575f80fd5b5061044360345481565b348015610ea3575f80fd5b506107af610eb2366004615252565b613623565b348015610ec2575f80fd5b506105637f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b181565b5f805f610ef56136d2565b909250905080610f0a64e8d4a5100084615468565b10610f1c5764e8d4a510009250505090565b610f26828261547b565b9250505090565b5f546001600160a01b0316331480610f6457505f80516020615ada833981519152546001600160a01b0316336001600160a01b0316145b610f895760405162461bcd60e51b8152600401610f809061548e565b60405180910390fd5b603454811015610fd55760405162461bcd60e51b815260206004820152601760248201527641524d3a2073656c6c20707269636520746f6f206c6f7760481b6044820152606401610f80565b60345482106110205760405162461bcd60e51b8152602060048201526017602482015276082a49a7440c4eaf240e0e4d2c6ca40e8dede40d0d2ced604b1b6044820152606401610f80565b806110396a0c097ce7bc90715b34b9f160241b806154e1565b61104391906154f8565b6032819055603383905560408051918252602082018490527fa2136948fd1e5333c2ee27c9e48848a560b693e6bbd18082623a738179ff295291015b60405180910390a15050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0380546060915f80516020615aba833981519152916110c990615517565b80601f01602080910402602001604051908101604052809291908181526020018280546110f590615517565b80156111405780601f1061111757610100808354040283529160200191611140565b820191905f5260205f20905b81548152906001019060200180831161112357829003601f168201915b505050505091505090565b5f6111747f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025490565b61117c610eea565b61118690846154e1565b61119091906154f8565b92915050565b6035545f90819081906111bb906001600160801b03600160801b820481169116615549565b6040516370a0823160e01b81523060048201526001600160801b039190911691505f906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa15801561122d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112519190615570565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416906370a0823190602401602060405180830381865afa1580156112b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112dc9190615570565b90508183116112f4576112ef838361547b565b6112f6565b5f5b94508093507f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031603611358579293925b5050509091565b5f3361136c818585613741565b5060019392505050565b61137e61374e565b603a80546001600160a01b0319166001600160a01b0383169081179091556040517fb8fd9afc34c38fcd13b9a3b7646482eb1fddcefb40af2c70609972816eba3208905f90a250565b6113cf61374e565b6113d8816137cf565b50565b5f336113e8858285613838565b6113f3858585613895565b506001949350505050565b61140661374e565b61142c6d629b8c891b267182b614000000006a0c097ce7bc90715b34b9f160241b61547b565b81101561147b5760405162461bcd60e51b815260206004820152601860248201527f41524d3a2063726f737320707269636520746f6f206c6f7700000000000000006044820152606401610f80565b6a0c097ce7bc90715b34b9f160241b8111156114d95760405162461bcd60e51b815260206004820152601960248201527f41524d3a2063726f737320707269636520746f6f2068696768000000000000006044820152606401610f80565b60325481906114f66a0c097ce7bc90715b34b9f160241b806154e1565b61150091906154f8565b10156115485760405162461bcd60e51b815260206004820152601760248201527641524d3a2073656c6c20707269636520746f6f206c6f7760481b6044820152606401610f80565b80603354106115935760405162461bcd60e51b8152602060048201526017602482015276082a49a7440c4eaf240e0e4d2c6ca40e8dede40d0d2ced604b1b6044820152606401610f80565b603454811015611677576040516370a0823160e01b815230600482015264e8d4a51000907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316906370a0823190602401602060405180830381865afa158015611606573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061162a9190615570565b106116775760405162461bcd60e51b815260206004820152601960248201527f41524d3a20746f6f206d616e79206261736520617373657473000000000000006044820152606401610f80565b60348190556040518181527f6f938e86fbdbe7829d0289b348cd9e528f2f17c705f469f4a17a0a2796e007d0906020015b60405180910390a150565b6060600284146117005760405162461bcd60e51b8152602060048201526018602482015277082a49a744092dcecc2d8d2c840e0c2e8d040d8cadccee8d60431b6044820152606401610f80565b611709826138f2565b5f85855f81811061171c5761171c615587565b905060200201602081019061173191906150bb565b90505f8686600181811061174757611747615587565b905060200201602081019061175c91906150bb565b90505f61176b83838c8961393a565b9050888110156117bd5760405162461bcd60e51b815260206004820152601f60248201527f41524d3a20496e73756666696369656e74206f757470757420616d6f756e74006044820152606401610f80565b604080516002808252606082018352909160208301908036833701905050935089845f815181106117f0576117f0615587565b602002602001018181525050808460018151811061181057611810615587565b6020026020010181815250505050509695505050505050565b5f6111908261114b565b5f546060906001600160a01b031633148061186d57505f80516020615ada833981519152546001600160a01b0316336001600160a01b0316145b6118895760405162461bcd60e51b8152600401610f809061548e565b604051636b34082160e11b81526001600160a01b037f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b1169063d6681042906118d9908690869030906004016155df565b5f604051808303815f875af11580156118f4573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261191b9190810190615684565b90505f805b8381101561199e5784848281811061193a5761193a615587565b905060200201358261194c9190615468565b915084848281811061196057611960615587565b9050602002013560655f85848151811061197c5761197c615587565b60209081029190910181015182528101919091526040015f2055600101611920565b508060645f8282546119b09190615468565b90915550506040517f3fdbeb02a84d41ebaf1c8edce1b73f1617e0d3675168dfeb8d86759c18782da4906119e990869086908690615714565b60405180910390a15092915050565b611a0061374e565b6113d881613ae3565b60605f611a188787878661393a565b905083811015611a6a5760405162461bcd60e51b815260206004820152601f60248201527f41524d3a20496e73756666696369656e74206f757470757420616d6f756e74006044820152606401610f80565b604080516002808252606082018352909160208301908036833701905050915084825f81518110611a9d57611a9d615587565b6020026020010181815250508082600181518110611abd57611abd615587565b6020026020010181815250505095945050505050565b5f611ade8383613b8b565b9392505050565b606060028414611b325760405162461bcd60e51b8152602060048201526018602482015277082a49a744092dcecc2d8d2c840e0c2e8d040d8cadccee8d60431b6044820152606401610f80565b611b3b826138f2565b5f85855f818110611b4e57611b4e615587565b9050602002016020810190611b6391906150bb565b90505f86866001818110611b7957611b79615587565b9050602002016020810190611b8e91906150bb565b90505f611b9d83838c89613d3d565b905088811115611bea5760405162461bcd60e51b81526020600482015260186024820152771054934e88115e18d95cdcc81a5b9c1d5d08185b5bdd5b9d60421b6044820152606401610f80565b604080516002808252606082018352909160208301908036833701905050935080845f81518110611c1d57611c1d615587565b602002602001018181525050898460018151811061181057611810615587565b5f611c535f80516020615ada8339815191525490565b905090565b5f611c616136d2565b50919050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060915f80516020615aba833981519152916110c990615517565b5f546001600160a01b0316331480611cdc57505f80516020615ada833981519152546001600160a01b0316336001600160a01b0316145b611cf85760405162461bcd60e51b8152600401610f809061548e565b670de0b6b3a7640000811115611d505760405162461bcd60e51b815260206004820152601760248201527f41524d3a20696e76616c69642061726d206275666665720000000000000000006044820152606401610f80565b603d8190556040518181527f29128dbcf994e1ddc09cdbce01c287bb3f6b0cf4dd3c98174cadbbaf67bc22d7906020016116a8565b611d8d61374e565b6113d881613ea3565b5f3361136c818585613895565b5f80611dae8361114b565b60365492509050611dc0826001615468565b6036556035545f90611de590611de09084906001600160801b0316615468565b613f42565b603580546001600160801b0319166001600160801b03831617905590505f611e2d7f000000000000000000000000000000000000000000000000000000000000025842615468565b90506040518060c00160405280336001600160a01b031681526020015f151581526020018264ffffffffff168152602001611e6785613f42565b6001600160801b03168152602001836001600160801b03168152602001611e8d87613f42565b6001600160801b039081169091525f8681526037602090815260409182902084518154928601519386015164ffffffffff16600160a81b0264ffffffffff60a81b19941515600160a01b026001600160a81b03199094166001600160a01b03909216919091179290921792909216178155606083015160808401518316600160801b0290831617600182015560a09092015160029092018054929091166001600160801b031992909216919091179055611f473386613f75565b611f58611f5384613fad565b613fd9565b60388054600290611f73908490620100009004600f0b615743565b82546101009290920a6001600160801b0381810219909316918316021790915560408051868152918516602083015264ffffffffff84169082015285915033907fc04c86cfd81036557541f9c68971ace59cbc9057ecab7d48874a6177ad117f4f9060600160405180910390a35050915091565b5f546001600160a01b031633148061201e57505f80516020615ada833981519152546001600160a01b0316336001600160a01b0316145b61203a5760405162461bcd60e51b8152600401610f809061548e565b6001600160a01b038116158061206757506001600160a01b0381165f908152603c602052604090205460ff165b6120af5760405162461bcd60e51b81526020600482015260196024820152781054934e881b585c9ad95d081b9bdd081cdd5c1c1bdc9d1959603a1b6044820152606401610f80565b603b546001600160a01b0390811690821681036120ca575050565b6001600160a01b038116156121c2576040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa15801561211d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121419190615570565b905080156121c057604051635d043b2960e11b815260048101829052306024820181905260448201526001600160a01b0383169063ba087652906064016020604051808303815f875af115801561219a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121be9190615570565b505b505b603b80546001600160a01b0319166001600160a01b0384169081179091556040517fe9f5fe520e5763f721d470ecb21b23763a3b0b9e720070111b1b935c1107b065905f90a26001600160a01b03821661221a575050565b61222261400d565b50505050565b603b545f9081906001600160a01b031661227c5760405162461bcd60e51b81526020600482015260156024820152741054934e881b9bc81858dd1a5d99481b585c9ad95d605a1b6044820152606401610f80565b61228461400d565b915091509091565b6040516370a0823160e01b81523060048201525f907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa1580156122f0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123149190615570565b6035546123319190600160801b90046001600160801b0316615468565b603b549091506001600160a01b031680156123b95760405163ce96cb7760e01b81523060048201526001600160a01b0382169063ce96cb7790602401602060405180830381865afa158015612388573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123ac9190615570565b6123b69083615468565b91505b5090565b6123c561374e565b6113d881614500565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156124125750825b90505f826001600160401b0316600114801561242d5750303b155b90508115801561243b575080155b156124595760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561248357845460ff60401b1916600160401b1785555b612493898e8e8e8e8d8d8d61454d565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b1811660048301525f1960248301527f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84169063095ea7b3906044016020604051808303815f875af115801561251f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612543919061577f565b50831561258a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b5f6111908233613b8b565b5f6125ad610eea565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace025461117c565b5f806125de6136d2565b90925090506126026125ef83613fad565b6125f883613fad565b611f539190615798565b603880546001600160801b0392909216620100000262010000600160901b03199092169190911790555f82900361263a575f91505090565b612643826147cf565b6040516370a0823160e01b81523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa1580156126a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126c99190615570565b8211156127185760405162461bcd60e51b815260206004820152601b60248201527f41524d3a20696e73756666696369656e74206c697175696469747900000000006044820152606401610f80565b60395460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018490527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29091169063a9059cbb906044016020604051808303815f875af1158015612789573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127ad919061577f565b506039546040518381526001600160a01b03909116907f06c5efeff5c320943d265dc4e5f1af95ad523555ce0c1957e367dda5514572df9060200160405180910390a25090565b60405163e3afe0a360e01b81526001600160a01b037f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b1169063e3afe0a3906128469087908790879087906004016157b7565b5f604051808303815f87803b15801561285d575f80fd5b505af115801561286f573d5f803e3d5ffd5b505050505f805b8481101561290f575f60655f88888581811061289457612894615587565b9050602002013581526020019081526020015f205490505f81116128fa5760405162461bcd60e51b815260206004820152601860248201527f4c69646f41524d3a20696e76616c6964207265717565737400000000000000006044820152606401610f80565b6129048184615468565b925050600101612876565b508060645f828254612921919061547b565b925050819055507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004015f604051808303818588803b158015612981575f80fd5b505af1158015612993573d5f803e3d5ffd5b50505050507fb7700a52345bff1ce6201d84f55fe81f2ea203b1b1bdc56a42571819aab2337a85856040516129c99291906157dd565b60405180910390a15050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460029190600160401b900460ff1680612a21575080546001600160401b03808416911610155b15612a3f5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b178155612a6861374e565b604051637d031b6560e01b81523060048201525f9081906001600160a01b037f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b11690637d031b65906024015f60405180830381865afa158015612acd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612af49190810190615684565b90505f7f000000000000000000000000889edc2edab5f40e902b864ad4d7ade8e412f9b16001600160a01b031663b8c4b85a836040518263ffffffff1660e01b8152600401612b439190615202565b5f60405180830381865afa158015612b5d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612b8491908101906157f8565b90505f5b8251811015612cef57818181518110612ba357612ba3615587565b602090810291909101015160a0015115612bff5760405162461bcd60e51b815260206004820152601860248201527f4c69646f41524d3a20616c726561647920636c61696d656400000000000000006044820152606401610f80565b306001600160a01b0316828281518110612c1b57612c1b615587565b6020026020010151604001516001600160a01b031614612c725760405162461bcd60e51b81526020600482015260126024820152712634b237a0a9269d103737ba1037bbb732b960711b6044820152606401610f80565b818181518110612c8457612c84615587565b60200260200101515f015160655f858481518110612ca457612ca4615587565b602002602001015181526020019081526020015f2081905550818181518110612ccf57612ccf615587565b60200260200101515f015184612ce59190615468565b9350600101612b88565b506064548314612d415760405162461bcd60e51b815260206004820152601960248201527f4c69646f41524d3a206d697373696e67207265717565737473000000000000006044820152606401610f80565b7f69eb7dc14e9da25ef5632680c43be64e8b99b47c76540e95986a1298e7616b918284604051612d729291906158e3565b60405180910390a15050815460ff60401b19168255506040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200161107f565b612dc961374e565b5f5b81811015612fe2575f838383818110612de657612de6615587565b9050602002016020810190612dfb91906150bb565b90506001600160a01b038116612e495760405162461bcd60e51b81526020600482015260136024820152721054934e881a5b9d985b1a59081b585c9ad95d606a1b6044820152606401610f80565b6001600160a01b0381165f908152603c602052604090205460ff1615612eb15760405162461bcd60e51b815260206004820152601d60248201527f41524d3a206d61726b657420616c726561647920737570706f727465640000006044820152606401610f80565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316816001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f3b9190615904565b6001600160a01b031614612f915760405162461bcd60e51b815260206004820152601960248201527f41524d3a20696e76616c6964206d61726b6574206173736574000000000000006044820152606401610f80565b6001600160a01b0381165f818152603c6020526040808220805460ff19166001179055517fbc600b1f03d316c479b49930c28e328809316458d5b5dacbb7419df5f6f896479190a250600101612dcb565b505050565b612fef61374e565b6001600160a01b03811661303b5760405162461bcd60e51b81526020600482015260136024820152721054934e881a5b9d985b1a59081b585c9ad95d606a1b6044820152606401610f80565b6001600160a01b0381165f908152603c602052604090205460ff1661309e5760405162461bcd60e51b81526020600482015260196024820152781054934e881b585c9ad95d081b9bdd081cdd5c1c1bdc9d1959603a1b6044820152606401610f80565b603b546001600160a01b03908116908216036130f45760405162461bcd60e51b815260206004820152601560248201527441524d3a206d61726b657420696e2061637469766560581b6044820152606401610f80565b6001600160a01b0381165f818152603c6020526040808220805460ff19169055517f59d7b1e52008dc342c9421dadfc773114b914a65682a4e4b53cf60a970df0d779190a250565b6001600160a01b039182165f9081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b5f818152603760209081526040808320815160c08101835281546001600160a01b0381168252600160a01b810460ff16151594820194909452600160a81b90930464ffffffffff1691830182905260018101546001600160801b038082166060860152600160801b909104811660808501526002909101541660a08301524210156132485760405162461bcd60e51b815260206004820152601360248201527210db185a5b4819195b185e481b9bdd081b595d606a1b6044820152606401610f80565b61325061228c565b81608001516001600160801b031611156132ac5760405162461bcd60e51b815260206004820152601760248201527f51756575652070656e64696e67206c69717569646974790000000000000000006044820152606401610f80565b80516001600160a01b031633146132f55760405162461bcd60e51b815260206004820152600d60248201526c2737ba103932b8bab2b9ba32b960991b6044820152606401610f80565b6020810151156133395760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b6044820152606401610f80565b5f808260a001516001600160801b0316116133615781606001516001600160801b0316613377565b6133778260a001516001600160801b031661114b565b90508082606001516001600160801b03161061339357806133a2565b81606001516001600160801b03165b5f858152603760205260409020805460ff60a01b1916600160a01b17905560608301519093506133da906001600160801b0316613f42565b603580546010906133fc908490600160801b90046001600160801b031661591f565b82546001600160801b039182166101009390930a928302919092021990911617905550603b546001600160a01b0316801561354b576040516370a0823160e01b81523060048201525f907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015613495573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134b99190615570565b905080851115613549575f6134ce828761547b565b604051632d182be560e21b815260048101829052306024820181905260448201529091506001600160a01b0384169063b460af94906064016020604051808303815f875af1158015613522573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135469190615570565b50505b505b60405163a9059cbb60e01b8152336004820152602481018590527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03169063a9059cbb906044016020604051808303815f875af11580156135b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135d9919061577f565b50604051848152859033907f36dd2c9b55f12509e3b5f4f4d765ddefc2776a28018b18da2335cf2ab93bb2689060200160405180910390a3505050919050565b5f611190826125a4565b60605f61363287878786613d3d565b90508381111561367f5760405162461bcd60e51b81526020600482015260186024820152771054934e88115e18d95cdcc81a5b9c1d5d08185b5bdd5b9d60421b6044820152606401610f80565b604080516002808252606082018352909160208301908036833701905050915080825f815181106136b2576136b2615587565b6020026020010181815250508482600181518110611abd57611abd615587565b5f806136dc6148e4565b506038549091505f90620100009004600f0b6136f783613fad565b6137019190615798565b90505f8113613712575f9250509091565b6038546127109061ffff1661372683614b81565b61373091906154e1565b61373a91906154f8565b9250509091565b612fe28383836001614ba6565b5f80516020615ada833981519152546001600160a01b0316336001600160a01b0316146137cd5760405162461bcd60e51b815260206004820152602760248201527f41524d3a204f6e6c79206f776e65722063616e2063616c6c20746869732066756044820152663731ba34b7b71760c91b6064820152608401610f80565b565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6138055f80516020615ada8339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a15f80516020615ada83398151915255565b5f613843848461313c565b90505f198114612222578181101561388757604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610f80565b61222284848484035f614ba6565b6001600160a01b0383166138be57604051634b637e8f60e11b81525f6004820152602401610f80565b6001600160a01b0382166138e75760405163ec442f0560e01b81525f6004820152602401610f80565b612fe2838383614c8a565b428110156113d85760405162461bcd60e51b81526020600482015260156024820152741054934e88111958591b1a5b9948195e1c1a5c9959605a1b6044820152606401610f80565b5f82816001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28116908816036139ca577f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316866001600160a01b0316146139c15760405162461bcd60e51b8152600401610f809061593f565b50603254613a9d565b7f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316876001600160a01b031603613a5d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316866001600160a01b031614613a545760405162461bcd60e51b8152600401610f809061593f565b50603354613a9d565b60405162461bcd60e51b815260206004820152601560248201527420a9269d1024b73b30b634b21034b7103a37b5b2b760591b6044820152606401610f80565b6a0c097ce7bc90715b34b9f160241b613ab682846154e1565b613ac091906154f8565b9250613ace87333088614dc3565b613ad9868585614e3b565b5050949350505050565b613af060026127106154f8565b811115613b335760405162461bcd60e51b8152602060048201526011602482015270082a49a7440cccaca40e8dede40d0d2ced607b1b6044820152606401610f80565b613b3b6125d4565b50613b4581614eed565b6038805461ffff191661ffff929092169190911790556040518181527f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c76906020016116a8565b5f613b95836125a4565b9050613ba3611f5384613fad565b60388054600290613bbe908490620100009004600f0b61596f565b82546001600160801b039182166101009390930a9283029190920219909116179055506040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906323b872dd906064016020604051808303815f875af1158015613c51573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c75919061577f565b50613c808282614f1b565b603a546001600160a01b031615613cf357603a54604051637dc46f6160e01b81526001600160a01b0384811660048301526024820186905290911690637dc46f61906044015f604051808303815f87803b158015613cdc575f80fd5b505af1158015613cee573d5f803e3d5ffd5b505050505b60408051848152602081018390526001600160a01b038416917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a292915050565b5f82816001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811690881603613dcd577f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316866001600160a01b031614613dc45760405162461bcd60e51b8152600401610f809061593f565b50603254613e5c565b7f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316876001600160a01b031603613a5d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316866001600160a01b031614613e575760405162461bcd60e51b8152600401610f809061593f565b506033545b80613e756a0c097ce7bc90715b34b9f160241b846154e1565b613e7f91906154f8565b613e8a906003615468565b9250613e9887333086614dc3565b613ad9868587614e3b565b6001600160a01b038116613ef95760405162461bcd60e51b815260206004820152601a60248201527f41524d3a20696e76616c69642066656520636f6c6c6563746f720000000000006044820152606401610f80565b603980546001600160a01b0319166001600160a01b0383169081179091556040517fe5693914d19c789bdee50a362998c0bc8d035a835f9871da5d51152f0582c34f905f90a250565b5f6001600160801b038211156123b9576040516306dfcc6560e41b81526080600482015260248101839052604401610f80565b6001600160a01b038216613f9e57604051634b637e8f60e11b81525f6004820152602401610f80565b613fa9825f83614c8a565b5050565b5f6001600160ff1b038211156123b95760405163123baf0360e11b815260048101839052602401610f80565b80600f81900b81146140085760405163327269a760e01b81526080600482015260248101839052604401610f80565b919050565b5f805f806140196148e4565b91509150815f0361402f57505f93849350915050565b5f670de0b6b3a7640000603d548461404791906154e1565b61405191906154f8565b90505f61405d83613fad565b6040516370a0823160e01b81523060048201526140ec907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa1580156140c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140e79190615570565b613fad565b6140f69190615798565b905061410182613fad565b61410b9082615798565b603b549096506001600160a01b03167f0000000000000000000000000000000000000000000000000de0b6b3a7640000871315614262575f61414c88614b81565b60405163095ea7b360e01b81526001600160a01b038481166004830152602482018390529192507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29091169063095ea7b3906044016020604051808303815f875af11580156141bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141e1919061577f565b50604051636e553f6560e01b8152600481018290523060248201526001600160a01b03831690636e553f65906044016020604051808303815f875af115801561422c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142509190615570565b5061425a81613fad565b9650506144b3565b5f8712156144b35760405163ce96cb7760e01b81523060048201525f906001600160a01b0383169063ce96cb7790602401602060405180830381865afa1580156142ae573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142d29190615570565b90505f6142e66142e18a61599c565b614b81565b90508082101561442557604051636c82bbbf60e11b81523060048201525f906001600160a01b0385169063d905777e90602401602060405180830381865afa158015614334573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906143589190615570565b90507f00000000000000000000000000000000000000000000000000000000009896808111614391575097985f98509650505050505050565b604051635d043b2960e11b815260048101829052306024820181905260448201525f906001600160a01b0386169063ba087652906064016020604051808303815f875af11580156143e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906144089190615570565b905061441381613fad565b61441c9061599c565b995050506144b0565b604051632d182be560e21b815260048101829052306024820181905260448201526001600160a01b0384169063b460af94906064016020604051808303815f875af1158015614476573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061449a9190615570565b506144a481613fad565b6144ad9061599c565b97505b50505b60408051888152602081018890526001600160a01b038316917f0538e1fc8a5bd2f2ae0c40c0a54b4208673263b92c883fe270768a5151346dfd910160405180910390a250505050509091565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f4721129e0e676ed6a92909bb24e853ccdd63ad72280cc2e974e38e480e0e6e54906020016116a8565b614556886123c5565b6145c787878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050604080516020601f8b0181900481028201810190925289815292508991508890819084018382808284375f92019190915250614f4f92505050565b6040516323b872dd60e01b815233600482015230602482015264e8d4a5100060448201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906323b872dd906064016020604051808303815f875af115801561463b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061465f919061577f565b5061467161dead64e8d4a51000614f1b565b6a0c097ce7bc90715b34b9f160241b603281905561469e906d629b8c891b267182b614000000009061547b565b60338190556032546040517fa2136948fd1e5333c2ee27c9e48848a560b693e6bbd18082623a738179ff2952926146dc928252602082015260400190565b60405180910390a15f6146ed6148e4565b5090506146fc611f5382613fad565b603880546001600160801b0392909216620100000262010000600160901b031990921691909117905561472e84613ae3565b61473783613ea3565b603a80546001600160a01b0319166001600160a01b0384169081179091556040517fb8fd9afc34c38fcd13b9a3b7646482eb1fddcefb40af2c70609972816eba3208905f90a26a0c097ce7bc90715b34b9f160241b60348190556040519081527f6f938e86fbdbe7829d0289b348cd9e528f2f17c705f469f4a17a0a2796e007d09060200160405180910390a1505050505050505050565b6035545f906147f0906001600160801b03600160801b820481169116615549565b6001600160801b03169050805f03614806575050565b6040516370a0823160e01b81523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015614868573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061488c9190615570565b6148968284615468565b1115613fa95760405162461bcd60e51b815260206004820152601b60248201527f41524d3a20496e73756666696369656e74206c697175696469747900000000006044820152606401610f80565b6040516370a0823160e01b81523060048201525f9081908190614976907f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84906001600160a01b038216906370a0823190602401602060405180830381865afa158015614952573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140089190615570565b90505f6a0c097ce7bc90715b34b9f160241b6034548361499691906154e1565b6149a091906154f8565b6064546040516370a0823160e01b81523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015614a05573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614a299190615570565b614a339190615468565b614a3d9190615468565b603b549091506001600160a01b03168015614b32576040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa158015614a96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614aba9190615570565b60405163266d6a8360e11b8152600481018290529091506001600160a01b03831690634cdad50690602401602060405180830381865afa158015614b00573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614b249190615570565b614b2e9084615468565b9250505b603554614b51906001600160801b03600160801b820481169116615549565b6001600160801b0316935083821015614b6e575f94505050509091565b614b78848361547b565b94505050509091565b5f808212156123b957604051635467221960e11b815260048101839052602401610f80565b5f80516020615aba8339815191526001600160a01b038516614bdd5760405163e602df0560e01b81525f6004820152602401610f80565b6001600160a01b038416614c0657604051634a1406b160e11b81525f6004820152602401610f80565b6001600160a01b038086165f90815260018301602090815260408083209388168352929052208390558115614c8357836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051614c7a91815260200190565b60405180910390a35b5050505050565b5f80516020615aba8339815191526001600160a01b038416614cc45781816002015f828254614cb99190615468565b90915550614d349050565b6001600160a01b0384165f9081526020829052604090205482811015614d165760405163391434e360e21b81526001600160a01b03861660048201526024810182905260448101849052606401610f80565b6001600160a01b0385165f9081526020839052604090209083900390555b6001600160a01b038316614d52576002810180548390039055614d70565b6001600160a01b0383165f9081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051614db591815260200190565b60405180910390a350505050565b6040516323b872dd60e01b81526001600160a01b0384811660048301528381166024830152604482018390528516906323b872dd906064016020604051808303815f875af1158015614e17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614c83919061577f565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316836001600160a01b031603614e7d57614e7d816147cf565b60405163a9059cbb60e01b81526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303815f875af1158015614ec9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612222919061577f565b5f61ffff8211156123b9576040516306dfcc6560e41b81526010600482015260248101839052604401610f80565b6001600160a01b038216614f445760405163ec442f0560e01b81525f6004820152602401610f80565b613fa95f8383614c8a565b614f57614f61565b613fa98282614faa565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166137cd57604051631afcd79f60e31b815260040160405180910390fd5b614fb2614f61565b5f80516020615aba8339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03614feb84826159fa565b506004810161222283826159fa565b5f806040838503121561500b575f80fd5b50508035926020909101359150565b5f602080835283518060208501525f5b818110156150465785810183015185820160400152820161502a565b505f604082860101526040601f19601f8301168501019250505092915050565b5f60208284031215615076575f80fd5b5035919050565b6001600160a01b03811681146113d8575f80fd5b5f80604083850312156150a2575f80fd5b82356150ad8161507d565b946020939093013593505050565b5f602082840312156150cb575f80fd5b8135611ade8161507d565b5f805f606084860312156150e8575f80fd5b83356150f38161507d565b925060208401356151038161507d565b929592945050506040919091013590565b5f8083601f840112615124575f80fd5b5081356001600160401b0381111561513a575f80fd5b6020830191508360208260051b8501011115615154575f80fd5b9250929050565b5f805f805f8060a08789031215615170575f80fd5b863595506020870135945060408701356001600160401b03811115615193575f80fd5b61519f89828a01615114565b90955093505060608701356151b38161507d565b80925050608087013590509295509295509295565b5f815180845260208085019450602084015f5b838110156151f7578151875295820195908201906001016151db565b509495945050505050565b602081525f611ade60208301846151c8565b5f8060208385031215615225575f80fd5b82356001600160401b0381111561523a575f80fd5b61524685828601615114565b90969095509350505050565b5f805f805f60a08688031215615266575f80fd5b85356152718161507d565b945060208601356152818161507d565b93506040860135925060608601359150608086013561529f8161507d565b809150509295509295909350565b5f80604083850312156152be575f80fd5b8235915060208301356152d08161507d565b809150509250929050565b5f8083601f8401126152eb575f80fd5b5081356001600160401b03811115615301575f80fd5b602083019150836020828501011115615154575f80fd5b5f805f805f805f8060c0898b03121561532f575f80fd5b88356001600160401b0380821115615345575f80fd5b6153518c838d016152db565b909a50985060208b0135915080821115615369575f80fd5b506153768b828c016152db565b909750955050604089013561538a8161507d565b93506060890135925060808901356153a18161507d565b915060a08901356153b18161507d565b809150509295985092959890939650565b5f805f80604085870312156153d5575f80fd5b84356001600160401b03808211156153eb575f80fd5b6153f788838901615114565b9096509450602087013591508082111561540f575f80fd5b5061541c87828801615114565b95989497509550505050565b5f8060408385031215615439575f80fd5b82356154448161507d565b915060208301356152d08161507d565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561119057611190615454565b8181038181111561119057611190615454565b60208082526033908201527f41524d3a204f6e6c79206f70657261746f72206f72206f776e65722063616e2060408201527231b0b636103a3434b990333ab731ba34b7b71760691b606082015260800190565b808202811582820484141761119057611190615454565b5f8261551257634e487b7160e01b5f52601260045260245ffd5b500490565b600181811c9082168061552b57607f821691505b602082108103611c6157634e487b7160e01b5f52602260045260245ffd5b6001600160801b0382811682821603908082111561556957615569615454565b5092915050565b5f60208284031215615580575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b8183525f6001600160fb1b038311156155c6575f80fd5b8260051b80836020870137939093016020019392505050565b604081525f6155f26040830185876155af565b905060018060a01b0383166020830152949350505050565b60405160c081016001600160401b038111828210171561562c5761562c61559b565b60405290565b604051601f8201601f191681016001600160401b038111828210171561565a5761565a61559b565b604052919050565b5f6001600160401b0382111561567a5761567a61559b565b5060051b60200190565b5f6020808385031215615695575f80fd5b82516001600160401b038111156156aa575f80fd5b8301601f810185136156ba575f80fd5b80516156cd6156c882615662565b615632565b81815260059190911b820183019083810190878311156156eb575f80fd5b928401925b82841015615709578351825292840192908401906156f0565b979650505050505050565b604081525f6157276040830185876155af565b828103602084015261573981856151c8565b9695505050505050565b600f82810b9082900b0360016001607f1b0319811260016001607f1b038213171561119057611190615454565b80518015158114614008575f80fd5b5f6020828403121561578f575f80fd5b611ade82615770565b8181035f83128015838313168383128216171561556957615569615454565b604081525f6157ca6040830186886155af565b82810360208401526157098185876155af565b602081525f6157f06020830184866155af565b949350505050565b5f6020808385031215615809575f80fd5b82516001600160401b0381111561581e575f80fd5b8301601f8101851361582e575f80fd5b805161583c6156c882615662565b81815260c0918202830184019184820191908884111561585a575f80fd5b938501935b838510156158d75780858a031215615875575f80fd5b61587d61560a565b8551815286860151878201526040808701516158988161507d565b908201526060868101519082015260806158b3818801615770565b9082015260a06158c4878201615770565b908201528352938401939185019161585f565b50979650505050505050565b604081525f6158f560408301856151c8565b90508260208301529392505050565b5f60208284031215615914575f80fd5b8151611ade8161507d565b6001600160801b0381811683821601908082111561556957615569615454565b60208082526016908201527520a9269d1024b73b30b634b21037baba103a37b5b2b760511b604082015260600190565b600f81810b9083900b0160016001607f1b03811360016001607f1b03198212171561119057611190615454565b5f600160ff1b82016159b0576159b0615454565b505f0390565b601f821115612fe257805f5260205f20601f840160051c810160208510156159db5750805b601f840160051c820191505b81811015614c83575f81556001016159e7565b81516001600160401b03811115615a1357615a1361559b565b615a2781615a218454615517565b846159b6565b602080601f831160018114615a5a575f8415615a435750858301515b5f19600386901b1c1916600185901b178555615ab1565b5f85815260208120601f198616915b82811015615a8857888601518255948401946001909101908401615a69565b5085821015615aa557878501515f19600388901b60f8161c191681555b505060018460011b0185555b50505050505056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a2646970667358221220627effa20e1babbc90b48743b7ffed09ad46c4364a018fea553c1b37467656fc64736f6c63430008170033

Verified Source Code Partial Match

Compiler: v0.8.23+commit.f704f362 EVM: shanghai Optimization: Yes (200 runs)
LidoARM.sol 184 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {AbstractARM} from "./AbstractARM.sol";
import {IERC20, IStETHWithdrawal, IWETH} from "./Interfaces.sol";

/**
 * @title Lido (stETH) Automated Redemption Manager (ARM)
 * @dev This implementation supports multiple Liquidity Providers (LPs) with single buy and sell prices.
 * It also integrates to a CapManager contract that caps the amount of assets a liquidity provider
 * can deposit and caps the ARM's total assets.
 * A performance fee is also collected on increases in the ARM's total assets.
 * @author Origin Protocol Inc
 */
contract LidoARM is Initializable, AbstractARM {
    /// @notice The address of the Lido stETH token
    IERC20 public immutable steth;
    /// @notice The address of the Wrapped ETH (WETH) token
    IWETH public immutable weth;
    /// @notice The address of the Lido Withdrawal Queue contract
    IStETHWithdrawal public immutable lidoWithdrawalQueue;

    /// @notice The amount of stETH in the Lido Withdrawal Queue
    uint256 public lidoWithdrawalQueueAmount;

    /// @notice stores the requested amount for each Lido withdrawal
    mapping(uint256 id => uint256 amount) public lidoWithdrawalRequests;

    event RequestLidoWithdrawals(uint256[] amounts, uint256[] requestIds);
    event ClaimLidoWithdrawals(uint256[] requestIds);
    event RegisterLidoWithdrawalRequests(uint256[] requestIds, uint256 totalAmountRequested);

    /// @param _steth The address of the stETH token
    /// @param _weth The address of the WETH token
    /// @param _lidoWithdrawalQueue The address of the Lido's withdrawal queue contract
    /// @param _claimDelay The delay in seconds before a user can claim a redeem from the request
    /// @param _minSharesToRedeem The minimum amount of shares to redeem from the active lending market
    /// @param _allocateThreshold The minimum amount of liquidity assets in excess of the ARM buffer before
    /// the ARM can allocate to a active lending market.
    constructor(
        address _steth,
        address _weth,
        address _lidoWithdrawalQueue,
        uint256 _claimDelay,
        uint256 _minSharesToRedeem,
        int256 _allocateThreshold
    ) AbstractARM(_weth, _steth, _weth, _claimDelay, _minSharesToRedeem, _allocateThreshold) {
        steth = IERC20(_steth);
        weth = IWETH(_weth);
        lidoWithdrawalQueue = IStETHWithdrawal(_lidoWithdrawalQueue);

        _disableInitializers();
    }

    /// @notice Initialize the storage variables stored in the proxy contract.
    /// The deployer that calls initialize has to approve the ARM's proxy contract to transfer 1e12 WETH.
    /// @param _name The name of the liquidity provider (LP) token.
    /// @param _symbol The symbol of the liquidity provider (LP) token.
    /// @param _operator The address of the account that can request and claim Lido withdrawals.
    /// @param _fee The performance fee that is collected by the feeCollector measured in basis points (1/100th of a percent).
    /// 10,000 = 100% performance fee
    /// 1,500 = 15% performance fee
    /// @param _feeCollector The account that can collect the performance fee
    /// @param _capManager The address of the CapManager contract
    function initialize(
        string calldata _name,
        string calldata _symbol,
        address _operator,
        uint256 _fee,
        address _feeCollector,
        address _capManager
    ) external initializer {
        _initARM(_operator, _name, _symbol, _fee, _feeCollector, _capManager);

        // Approve the Lido withdrawal queue contract. Used for redemption requests.
        steth.approve(address(lidoWithdrawalQueue), type(uint256).max);
    }

    /**
     * @notice Register the Lido withdrawal requests to the ARM contract.
     * This can only be called once by the contract Owner.
     */
    function registerLidoWithdrawalRequests() external reinitializer(2) onlyOwner {
        uint256 totalAmountRequested = 0;
        // Get all the ARM's outstanding withdrawal requests
        uint256[] memory requestIds = IStETHWithdrawal(lidoWithdrawalQueue).getWithdrawalRequests(address(this));
        // Get the status of all the withdrawal requests. eg amount, owner, claimed status
        IStETHWithdrawal.WithdrawalRequestStatus[] memory statuses =
            IStETHWithdrawal(lidoWithdrawalQueue).getWithdrawalStatus(requestIds);

        for (uint256 i = 0; i < requestIds.length; i++) {
            // The following should always be true given the requestIds came from calling getWithdrawalRequests
            require(statuses[i].isClaimed == false, "LidoARM: already claimed");
            require(statuses[i].owner == address(this), "LidoARM: not owner");

            // Store the amount of stETH of each Lido withdraw request
            lidoWithdrawalRequests[requestIds[i]] = statuses[i].amountOfStETH;
            totalAmountRequested += statuses[i].amountOfStETH;
        }

        require(totalAmountRequested == lidoWithdrawalQueueAmount, "LidoARM: missing requests");

        emit RegisterLidoWithdrawalRequests(requestIds, totalAmountRequested);
    }

    /**
     * @notice Request a stETH for ETH withdrawal.
     * Reference: https://docs.lido.fi/contracts/withdrawal-queue-erc721/
     * Note: There is a 1k amount limit. Caller should split large withdrawals in chunks of less or equal to 1k each.)
     */
    function requestLidoWithdrawals(uint256[] calldata amounts)
        external
        onlyOperatorOrOwner
        returns (uint256[] memory requestIds)
    {
        requestIds = lidoWithdrawalQueue.requestWithdrawals(amounts, address(this));

        // Sum the total amount of stETH being withdraw
        uint256 totalAmountRequested = 0;
        for (uint256 i = 0; i < amounts.length; i++) {
            totalAmountRequested += amounts[i];

            // Store the amount of each withdrawal request
            lidoWithdrawalRequests[requestIds[i]] = amounts[i];
        }

        // Increase the Ether outstanding from the Lido Withdrawal Queue
        lidoWithdrawalQueueAmount += totalAmountRequested;

        emit RequestLidoWithdrawals(amounts, requestIds);
    }

    /**
     * @notice Claim the ETH owed from the redemption requests and convert it to WETH.
     * Before calling this method, caller should check on the request NFTs to ensure the withdrawal was processed.
     * Withdrawal NFTs that have been transferred in from another account will be reverted.
     * @param requestIds The request IDs of the withdrawal requests.
     * @param hintIds The hint IDs of the withdrawal requests.
     * Call `findCheckpointHints` on the Lido withdrawal queue contract to get the hint IDs.
     */
    function claimLidoWithdrawals(uint256[] calldata requestIds, uint256[] calldata hintIds) external {
        // Claim the NFTs for ETH.
        lidoWithdrawalQueue.claimWithdrawals(requestIds, hintIds);

        // Reduce the amount outstanding from the Lido Withdrawal Queue.
        // The amount of ETH claimed from the Lido Withdrawal Queue can be less than the requested amount
        // in the event of a mass slashing event of Lido validators.
        uint256 totalAmountRequested = 0;
        for (uint256 i = 0; i < requestIds.length; i++) {
            // Read the requested amount from storage
            uint256 requestAmount = lidoWithdrawalRequests[requestIds[i]];

            // Validate the request came from this Lido ARM contract and not
            // transferred in from another account.
            require(requestAmount > 0, "LidoARM: invalid request");

            totalAmountRequested += requestAmount;
        }

        // Store the reduced outstanding withdrawals from the Lido Withdrawal Queue
        // Since withdrawal NFTs that have been transferred in from another account are reverted above,
        // this subtraction should never underflow.
        lidoWithdrawalQueueAmount -= totalAmountRequested;

        // Wrap all the received ETH to WETH. This can be less than the requested amount in the event of slashing.
        weth.deposit{value: address(this).balance}();

        emit ClaimLidoWithdrawals(requestIds);
    }

    /**
     * @dev Calculates the amount of WETH expected to be returned from the Lido Withdrawal Queue.
     * The actual amount returned can be less in the event of a slashing.
     */
    function _externalWithdrawQueue() internal view override returns (uint256) {
        return lidoWithdrawalQueueAmount;
    }

    /// @notice This payable method is necessary for receiving ETH claimed from the Lido withdrawal queue.
    receive() external payable {}
}
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
        }
    }
}
SafeCast.sol 1153 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}
AbstractARM.sol 1043 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {OwnableOperable} from "./OwnableOperable.sol";
import {IERC20, ICapManager} from "./Interfaces.sol";

/**
 * @title Generic Automated Redemption Manager (ARM)
 * @author Origin Protocol Inc
 */
abstract contract AbstractARM is OwnableOperable, ERC20Upgradeable {
    ////////////////////////////////////////////////////
    ///                 Constants
    ////////////////////////////////////////////////////

    /// @notice Maximum amount the Owner can set the cross price below 1 scaled to 36 decimals.
    /// 20e32 is a 0.2% deviation, or 20 basis points.
    uint256 public constant MAX_CROSS_PRICE_DEVIATION = 20e32;
    /// @notice Scale of the prices.
    uint256 public constant PRICE_SCALE = 1e36;
    /// @notice The amount of shares that are minted to a dead address on initialization
    uint256 internal constant MIN_TOTAL_SUPPLY = 1e12;
    /// @notice The address with no known private key that the initial shares are minted to
    address internal constant DEAD_ACCOUNT = 0x000000000000000000000000000000000000dEaD;
    /// @notice The scale of the performance fee
    /// 10,000 = 100% performance fee
    uint256 public constant FEE_SCALE = 10000;

    ////////////////////////////////////////////////////
    ///             Immutable Variables
    ////////////////////////////////////////////////////
    /// @notice The minimum amount of shares that can be redeemed from the active market.
    uint256 public immutable minSharesToRedeem;
    /// @notice The minimum amount of liquidity assets in excess of the ARM buffer before
    /// the ARM can allocate to a active lending market.
    /// This should be close to zero.
    /// @dev This prevents allocate flipping between depositing/withdrawing to/from the active market
    int256 public immutable allocateThreshold;
    /// @notice The address of the asset that is used to add and remove liquidity. eg WETH
    /// This is also the quote asset when the prices are set.
    /// eg the stETH/WETH price has a base asset of stETH and quote asset of WETH.
    address public immutable liquidityAsset;
    /// @notice The asset being purchased by the ARM and put in the withdrawal queue. eg stETH
    address public immutable baseAsset;
    /// @notice The swap input token that is transferred to this contract.
    /// From a User perspective, this is the token being sold.
    /// token0 is also compatible with the Uniswap V2 Router interface.
    IERC20 public immutable token0;
    /// @notice The swap output token that is transferred from this contract.
    /// From a User perspective, this is the token being bought.
    /// token1 is also compatible with the Uniswap V2 Router interface.
    IERC20 public immutable token1;
    /// @notice The delay before a withdrawal request can be claimed in seconds. eg 600 is 10 minutes.
    uint256 public immutable claimDelay;

    ////////////////////////////////////////////////////
    ///             Storage Variables
    ////////////////////////////////////////////////////

    /**
     * @notice For one `token0` from a Trader, how many `token1` does the pool send.
     * For example, if `token0` is WETH and `token1` is stETH then
     * `traderate0` is the WETH/stETH price.
     * From a Trader's perspective, this is the buy price.
     * From the ARM's perspective, this is the sell price.
     * Rate is to 36 decimals (1e36).
     * To convert to a stETH/WETH price, use `PRICE_SCALE * PRICE_SCALE / traderate0`.
     */
    uint256 public traderate0;
    /**
     * @notice For one `token1` from a Trader, how many `token0` does the pool send.
     * For example, if `token0` is WETH and `token1` is stETH then
     * `traderate1` is the stETH/WETH price.
     * From a Trader's perspective, this is the sell price.
     * From a ARM's perspective, this is the buy price.
     * Rate is to 36 decimals (1e36).
     */
    uint256 public traderate1;
    /// @notice The price that buy and sell prices can not cross scaled to 36 decimals.
    /// This is also the price the base assets, eg stETH, in the ARM contract are priced at in `totalAssets`.
    uint256 public crossPrice;

    /// @notice Cumulative total of all withdrawal requests including the ones that have already been claimed.
    uint128 public withdrawsQueued;
    /// @notice Total of all the withdrawal requests that have been claimed.
    uint128 public withdrawsClaimed;
    /// @notice Index of the next withdrawal request starting at 0.
    uint256 public nextWithdrawalIndex;

    struct WithdrawalRequest {
        address withdrawer;
        bool claimed;
        // When the withdrawal can be claimed
        uint40 claimTimestamp;
        // Amount of liquidity assets to withdraw. eg WETH
        uint128 assets;
        // Cumulative total of all withdrawal requests including this one when the redeem request was made.
        uint128 queued;
        // The amount of shares that were burned at the time of this request.
        // This has been added with a contract upgrade so may be zero for older requests.
        uint128 shares;
    }

    /// @notice Mapping of withdrawal request indices to the user withdrawal request data.
    mapping(uint256 requestId => WithdrawalRequest) public withdrawalRequests;

    /// @notice Performance fee that is collected by the feeCollector measured in basis points (1/100th of a percent).
    /// 10,000 = 100% performance fee
    /// 2,000 = 20% performance fee
    /// 500 = 5% performance fee
    uint16 public fee;
    /// @notice The available assets the last time the performance fees were collected and adjusted
    /// for liquidity assets (WETH) deposited and redeemed.
    /// This can be negative if there were asset gains and then all the liquidity providers redeemed.
    int128 public lastAvailableAssets;
    /// @notice The account or contract that can collect the performance fee.
    address public feeCollector;
    /// @notice The address of the CapManager contract used to manage the ARM's liquidity provider and total assets caps.
    address public capManager;

    /// @notice The address of the active lending market.
    address public activeMarket;
    /// @notice Lending markets that can be used by the ARM.
    mapping(address market => bool supported) public supportedMarkets;
    /// @notice Percentage of available liquid assets to keep in the ARM. 100% = 1e18.
    uint256 public armBuffer;

    uint256[38] private _gap;

    ////////////////////////////////////////////////////
    ///                 Events
    ////////////////////////////////////////////////////

    event TraderateChanged(uint256 traderate0, uint256 traderate1);
    event CrossPriceUpdated(uint256 crossPrice);
    event Deposit(address indexed owner, uint256 assets, uint256 shares);
    event RedeemRequested(
        address indexed withdrawer, uint256 indexed requestId, uint256 assets, uint256 queued, uint256 claimTimestamp
    );
    event RedeemClaimed(address indexed withdrawer, uint256 indexed requestId, uint256 assets);
    event FeeCollected(address indexed feeCollector, uint256 fee);
    event FeeUpdated(uint256 fee);
    event FeeCollectorUpdated(address indexed newFeeCollector);
    event CapManagerUpdated(address indexed capManager);
    event ActiveMarketUpdated(address indexed market);
    event MarketAdded(address indexed market);
    event MarketRemoved(address indexed market);
    event ARMBufferUpdated(uint256 armBuffer);
    event Allocated(address indexed market, int256 targetLiquidityDelta, int256 actualLiquidityDelta);

    constructor(
        address _token0,
        address _token1,
        address _liquidityAsset,
        uint256 _claimDelay,
        uint256 _minSharesToRedeem,
        int256 _allocateThreshold
    ) {
        require(IERC20(_token0).decimals() == 18);
        require(IERC20(_token1).decimals() == 18);

        token0 = IERC20(_token0);
        token1 = IERC20(_token1);

        claimDelay = _claimDelay;

        _setOwner(address(0)); // Revoke owner for implementation contract at deployment

        require(_liquidityAsset == address(token0) || _liquidityAsset == address(token1), "invalid liquidity asset");
        liquidityAsset = _liquidityAsset;
        // The base asset, eg stETH, is not the liquidity asset, eg WETH
        baseAsset = _liquidityAsset == _token0 ? _token1 : _token0;
        minSharesToRedeem = _minSharesToRedeem;

        require(_allocateThreshold >= 0, "invalid allocate threshold");
        allocateThreshold = _allocateThreshold;
    }

    /// @notice Initialize the contract.
    /// The deployer that calls initialize has to approve the this ARM's proxy contract to transfer 1e12 WETH.
    /// @param _operator The address of the account that can request and claim Lido withdrawals.
    /// @param _name The name of the liquidity provider (LP) token.
    /// @param _symbol The symbol of the liquidity provider (LP) token.
    /// @param _fee The performance fee that is collected by the feeCollector measured in basis points (1/100th of a percent).
    /// 10,000 = 100% performance fee
    /// 500 = 5% performance fee
    /// @param _feeCollector The account that can collect the performance fee
    /// @param _capManager The address of the CapManager contract
    function _initARM(
        address _operator,
        string calldata _name,
        string calldata _symbol,
        uint256 _fee,
        address _feeCollector,
        address _capManager
    ) internal {
        _initOwnableOperable(_operator);

        __ERC20_init(_name, _symbol);

        // Transfer a small bit of liquidity from the initializer to this contract
        IERC20(liquidityAsset).transferFrom(msg.sender, address(this), MIN_TOTAL_SUPPLY);

        // mint a small amount of shares to a dead account so the total supply can never be zero
        // This avoids donation attacks when there are no assets in the ARM contract
        _mint(DEAD_ACCOUNT, MIN_TOTAL_SUPPLY);

        // Set the sell price to its highest value. 1.0
        traderate0 = PRICE_SCALE;
        // Set the buy price to its lowest value. 0.998
        traderate1 = PRICE_SCALE - MAX_CROSS_PRICE_DEVIATION;
        emit TraderateChanged(traderate0, traderate1);

        // Initialize the last available assets to the current available assets
        // This ensures no performance fee is accrued when the performance fee is calculated when the fee is set
        (uint256 availableAssets,) = _availableAssets();
        lastAvailableAssets = SafeCast.toInt128(SafeCast.toInt256(availableAssets));
        _setFee(_fee);
        _setFeeCollector(_feeCollector);

        capManager = _capManager;
        emit CapManagerUpdated(_capManager);

        crossPrice = PRICE_SCALE;
        emit CrossPriceUpdated(PRICE_SCALE);
    }

    ////////////////////////////////////////////////////
    ///                 Swap Functions
    ////////////////////////////////////////////////////

    /**
     * @notice Swaps an exact amount of input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param to Recipient of the output tokens.
     * @return amounts The input and output token amounts.
     */
    function swapExactTokensForTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external virtual returns (uint256[] memory amounts) {
        uint256 amountOut = _swapExactTokensForTokens(inToken, outToken, amountIn, to);
        require(amountOut >= amountOutMin, "ARM: Insufficient output amount");

        amounts = new uint256[](2);
        amounts[0] = amountIn;
        amounts[1] = amountOut;
    }

    /**
     * @notice Uniswap V2 Router compatible interface. Swaps an exact amount of
     * input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual returns (uint256[] memory amounts) {
        require(path.length == 2, "ARM: Invalid path length");
        _inDeadline(deadline);

        IERC20 inToken = IERC20(path[0]);
        IERC20 outToken = IERC20(path[1]);

        uint256 amountOut = _swapExactTokensForTokens(inToken, outToken, amountIn, to);

        require(amountOut >= amountOutMin, "ARM: Insufficient output amount");

        amounts = new uint256[](2);
        amounts[0] = amountIn;
        amounts[1] = amountOut;
    }

    /**
     * @notice Receive an exact amount of output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param to Recipient of the output tokens.
     * @return amounts The input and output token amounts.
     */
    function swapTokensForExactTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountOut,
        uint256 amountInMax,
        address to
    ) external virtual returns (uint256[] memory amounts) {
        uint256 amountIn = _swapTokensForExactTokens(inToken, outToken, amountOut, to);

        require(amountIn <= amountInMax, "ARM: Excess input amount");

        amounts = new uint256[](2);
        amounts[0] = amountIn;
        amounts[1] = amountOut;
    }

    /**
     * @notice Uniswap V2 Router compatible interface. Receive an exact amount of
     * output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual returns (uint256[] memory amounts) {
        require(path.length == 2, "ARM: Invalid path length");
        _inDeadline(deadline);

        IERC20 inToken = IERC20(path[0]);
        IERC20 outToken = IERC20(path[1]);

        uint256 amountIn = _swapTokensForExactTokens(inToken, outToken, amountOut, to);

        require(amountIn <= amountInMax, "ARM: Excess input amount");

        amounts = new uint256[](2);
        amounts[0] = amountIn;
        amounts[1] = amountOut;
    }

    function _inDeadline(uint256 deadline) internal view {
        require(deadline >= block.timestamp, "ARM: Deadline expired");
    }

    /// @dev Ensure any liquidity assets reserved for the withdrawal queue are not used
    /// in swaps that send liquidity assets out of the ARM
    function _transferAsset(address asset, address to, uint256 amount) internal virtual {
        if (asset == liquidityAsset) _requireLiquidityAvailable(amount);

        IERC20(asset).transfer(to, amount);
    }

    /// @dev Hook to transfer assets into the ARM contract
    function _transferAssetFrom(address asset, address from, address to, uint256 amount) internal virtual {
        IERC20(asset).transferFrom(from, to, amount);
    }

    function _swapExactTokensForTokens(IERC20 inToken, IERC20 outToken, uint256 amountIn, address to)
        internal
        virtual
        returns (uint256 amountOut)
    {
        // Convert base asset to liquid asset or vice versa if needed
        uint256 convertedAmountIn = _convert(address(inToken), amountIn);

        uint256 price;
        if (inToken == token0) {
            require(outToken == token1, "ARM: Invalid out token");
            price = traderate0;
        } else if (inToken == token1) {
            require(outToken == token0, "ARM: Invalid out token");
            price = traderate1;
        } else {
            revert("ARM: Invalid in token");
        }
        amountOut = convertedAmountIn * price / PRICE_SCALE;

        // Transfer the input tokens from the caller to this ARM contract
        _transferAssetFrom(address(inToken), msg.sender, address(this), amountIn);

        // Transfer the output tokens to the recipient
        _transferAsset(address(outToken), to, amountOut);
    }

    function _swapTokensForExactTokens(IERC20 inToken, IERC20 outToken, uint256 amountOut, address to)
        internal
        virtual
        returns (uint256 amountIn)
    {
        // Convert base asset to liquid asset or vice versa if needed
        uint256 convertedAmountOut = _convert(address(outToken), amountOut);

        uint256 price;
        if (inToken == token0) {
            require(outToken == token1, "ARM: Invalid out token");
            price = traderate0;
        } else if (inToken == token1) {
            require(outToken == token0, "ARM: Invalid out token");
            price = traderate1;
        } else {
            revert("ARM: Invalid in token");
        }
        // always round in our favor
        // +1 for truncation when dividing integers
        // +2 to cover stETH transfers being up to 2 wei short of the requested transfer amount
        amountIn = ((convertedAmountOut * PRICE_SCALE) / price) + 3;

        // Transfer the input tokens from the caller to this ARM contract
        _transferAssetFrom(address(inToken), msg.sender, address(this), amountIn);

        // Transfer the output tokens to the recipient
        _transferAsset(address(outToken), to, amountOut);
    }

    /// @dev Convert between base asset and liquidity asset if needed.
    /// @param token The address of the token to convert from.
    /// @param amount The amount of the token to convert from.
    /// @return The converted to amount.
    /// Defaults to 1:1 conversion.
    /// This can be overridden if the base asset appreciates relative to the liquidity asset.
    /// For example, wstETH to WETH, weETH to WETH, sUSDe to USDe or wOETH to WETH.
    function _convert(address token, uint256 amount) internal view virtual returns (uint256) {
        return amount;
    }

    /// @notice Get the available liquidity for a each token in the ARM.
    /// @return reserve0 The available liquidity for token0
    /// @return reserve1 The available liquidity for token1
    function getReserves() external view returns (uint256 reserve0, uint256 reserve1) {
        // The amount of liquidity assets (WETH) that is still to be claimed in the withdrawal queue
        uint256 outstandingWithdrawals = withdrawsQueued - withdrawsClaimed;

        uint256 liquidityAssetBalance = IERC20(liquidityAsset).balanceOf(address(this));
        uint256 baseAssetBalance = IERC20(baseAsset).balanceOf(address(this));

        // Ensure there is no negative reserves when there are more outstanding withdrawals than liquidity assets in the ARM
        reserve0 = outstandingWithdrawals > liquidityAssetBalance ? 0 : liquidityAssetBalance - outstandingWithdrawals;
        reserve1 = baseAssetBalance;

        // The previous assignment assumed token0 is be the liquidity asset.
        // If not, swap the reserves
        if (address(token0) == baseAsset) (reserve0, reserve1) = (reserve1, reserve0);
    }

    ////////////////////////////////////////////////////
    ///         Swap Admin Functions
    ////////////////////////////////////////////////////

    /**
     * @notice Set exchange rates from an operator account from the ARM's perspective.
     * If token 0 is WETH and token 1 is stETH, then both prices will be set using the stETH/WETH price.
     * @param buyT1 The price the ARM buys Token 1 (stETH) from the Trader, denominated in Token 0 (WETH), scaled to 36 decimals.
     * From the Trader's perspective, this is the sell price.
     * @param sellT1 The price the ARM sells Token 1 (stETH) to the Trader, denominated in Token 0 (WETH), scaled to 36 decimals.
     * From the Trader's perspective, this is the buy price.
     */
    function setPrices(uint256 buyT1, uint256 sellT1) external onlyOperatorOrOwner {
        // Ensure buy price is always below past sell prices
        require(sellT1 >= crossPrice, "ARM: sell price too low");
        require(buyT1 < crossPrice, "ARM: buy price too high");

        traderate0 = PRICE_SCALE * PRICE_SCALE / sellT1; // quote (t0) -> base (t1); eg WETH -> stETH
        traderate1 = buyT1; // base (t1) -> quote (t0). eg stETH -> WETH

        emit TraderateChanged(traderate0, traderate1);
    }

    /**
     * @notice set the price that buy and sell prices can not cross.
     * That is, the buy prices must be below the cross price
     * and the sell prices must be above the cross price.
     * If the cross price is being lowered, there can not be a significant amount of base assets in the ARM. eg stETH.
     * This prevents the ARM making a loss when the base asset is sold at a lower price than it was bought
     * before the cross price was lowered.
     * The base assets should be sent to the withdrawal queue before the cross price can be lowered. For example, the
     * `Owner` should construct a tx that calls `requestLidoWithdrawals` before `setCrossPrice` for the Lido ARM
     * when the cross price is being lowered.
     * The cross price can be increased with assets in the ARM.
     * @param newCrossPrice The new cross price scaled to 36 decimals.
     */
    function setCrossPrice(uint256 newCrossPrice) external onlyOwner {
        require(newCrossPrice >= PRICE_SCALE - MAX_CROSS_PRICE_DEVIATION, "ARM: cross price too low");
        require(newCrossPrice <= PRICE_SCALE, "ARM: cross price too high");
        // The exiting sell price must be greater than or equal to the new cross price
        require(PRICE_SCALE * PRICE_SCALE / traderate0 >= newCrossPrice, "ARM: sell price too low");
        // The existing buy price must be less than the new cross price
        require(traderate1 < newCrossPrice, "ARM: buy price too high");

        // If the cross price is being lowered, there can not be a significant amount of base assets in the ARM. eg stETH.
        // This prevents the ARM making a loss when the base asset is sold at a lower price than it was bought
        // before the cross price was lowered.
        if (newCrossPrice < crossPrice) {
            // Check there is not a significant amount of base assets in the ARM
            require(IERC20(baseAsset).balanceOf(address(this)) < MIN_TOTAL_SUPPLY, "ARM: too many base assets");
        }

        // Save the new cross price to storage
        crossPrice = newCrossPrice;

        emit CrossPriceUpdated(newCrossPrice);
    }

    ////////////////////////////////////////////////////
    ///         Liquidity Provider Functions
    ////////////////////////////////////////////////////

    /// @notice Preview the amount of shares that would be minted for a given amount of assets
    /// @param assets The amount of liquidity assets to deposit
    /// @return shares The amount of shares that would be minted
    function previewDeposit(uint256 assets) external view returns (uint256 shares) {
        shares = convertToShares(assets);
    }

    /// @notice deposit liquidity assets in exchange for liquidity provider (LP) shares.
    /// The caller needs to have approved the contract to transfer the assets.
    /// @param assets The amount of liquidity assets to deposit
    /// @return shares The amount of shares that were minted
    function deposit(uint256 assets) external returns (uint256 shares) {
        shares = _deposit(assets, msg.sender);
    }

    /// @notice deposit liquidity assets in exchange for liquidity provider (LP) shares.
    /// Funds will be transferred from msg.sender.
    /// @param assets The amount of liquidity assets to deposit
    /// @param receiver The address that will receive shares.
    /// @return shares The amount of shares that were minted
    function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
        shares = _deposit(assets, receiver);
    }

    /// @dev Internal logic for depositing liquidity assets in exchange for liquidity provider (LP) shares.
    function _deposit(uint256 assets, address receiver) internal returns (uint256 shares) {
        // Calculate the amount of shares to mint after the performance fees have been accrued
        // which reduces the available assets, and before new assets are deposited.
        shares = convertToShares(assets);

        // Add the deposited assets to the last available assets
        lastAvailableAssets += SafeCast.toInt128(SafeCast.toInt256(assets));

        // Transfer the liquidity asset from the sender to this contract
        IERC20(liquidityAsset).transferFrom(msg.sender, address(this), assets);

        // mint shares
        _mint(receiver, shares);

        // Check the liquidity provider caps after the new assets have been deposited
        if (capManager != address(0)) {
            ICapManager(capManager).postDepositHook(receiver, assets);
        }

        emit Deposit(receiver, assets, shares);
    }

    /// @notice Preview the amount of assets that would be received for burning a given amount of shares
    /// @param shares The amount of shares to burn
    /// @return assets The amount of liquidity assets that would be received
    function previewRedeem(uint256 shares) external view returns (uint256 assets) {
        assets = convertToAssets(shares);
    }

    /// @notice Request to redeem liquidity provider shares for liquidity assets
    /// @param shares The amount of shares the redeemer wants to burn for liquidity assets
    /// @return requestId The index of the withdrawal request
    /// @return assets The max amount of liquidity assets that will be claimable by the redeemer.
    /// The amount can be less at claim time if ARM's assets per share has decreased. This can happen
    /// from a significant slashing event on the base asset, eg stETH.
    function requestRedeem(uint256 shares) external returns (uint256 requestId, uint256 assets) {
        // Calculate the amount of assets to transfer to the redeemer
        assets = convertToAssets(shares);

        requestId = nextWithdrawalIndex;
        // Store the next withdrawal request
        nextWithdrawalIndex = requestId + 1;

        uint128 queued = SafeCast.toUint128(withdrawsQueued + assets);
        // Store the updated queued amount which reserves liquidity assets (WETH) in the withdrawal queue
        withdrawsQueued = queued;

        uint40 claimTimestamp = uint40(block.timestamp + claimDelay);

        // Store requests
        withdrawalRequests[requestId] = WithdrawalRequest({
            withdrawer: msg.sender,
            claimed: false,
            claimTimestamp: claimTimestamp,
            assets: SafeCast.toUint128(assets),
            queued: queued,
            shares: SafeCast.toUint128(shares)
        });

        // burn redeemer's shares
        _burn(msg.sender, shares);

        // Remove the redeemed assets from the last available assets
        lastAvailableAssets -= SafeCast.toInt128(SafeCast.toInt256(assets));

        emit RedeemRequested(msg.sender, requestId, assets, queued, claimTimestamp);
    }

    /// @notice Claim liquidity assets from a previous withdrawal request after the claim delay has passed.
    /// This will try and withdraw from the active lending market if there are not enough liquidity assets in the ARM.
    /// If there is not enough liquidity in the ARM and lending market the transaction will revert.
    /// If the lending market has enough liquidity but has high utilization preventing the withdrawal, the transaction will revert.
    /// If the assets per shares has decreased since the redeem request, the asset value of the redeemed shares at claim is used.
    /// @param requestId The index of the withdrawal request
    /// @return assets The amount of liquidity assets that were transferred to the redeemer
    function claimRedeem(uint256 requestId) external returns (uint256 assets) {
        // Load the struct from storage into memory
        WithdrawalRequest memory request = withdrawalRequests[requestId];

        require(request.claimTimestamp <= block.timestamp, "Claim delay not met");
        // Is there enough liquidity in the ARM and lending market to claim this request?
        require(request.queued <= claimable(), "Queue pending liquidity");
        require(request.withdrawer == msg.sender, "Not requester");
        require(request.claimed == false, "Already claimed");

        // In the scenario where the ARM has made a loss from after the redeem request, the asset value of
        // the redeemed shares at the time of the claim is used.
        // This can happen if there was a significant slashing event on the base asset, eg stETH, after the redeem request was made.
        uint256 assetsAtClaim = request.shares > 0 ? convertToAssets(request.shares) : request.assets;
        // Use the minimum of the asset value of the redeemed shares at request or claim.
        assets = request.assets < assetsAtClaim ? request.assets : assetsAtClaim;

        // Store the request as claimed
        withdrawalRequests[requestId].claimed = true;
        // Store the updated claimed amount
        // The asset value at the time of the request is used instead of the value at the time of claim
        // as the queued amount used the value at the time of the request.
        withdrawsClaimed += SafeCast.toUint128(request.assets);

        // If there is not enough liquidity assets in the ARM, get from the active market if one is configured.
        // Read the active market address from storage once to save gas.
        address activeMarketMem = activeMarket;
        if (activeMarketMem != address(0)) {
            uint256 liquidityInARM = IERC20(liquidityAsset).balanceOf(address(this));

            if (assets > liquidityInARM) {
                uint256 liquidityFromMarket = assets - liquidityInARM;
                // This should work as we have checked earlier the claimable() amount which includes the active market
                IERC4626(activeMarketMem).withdraw(liquidityFromMarket, address(this), address(this));
            }
        }

        // transfer the liquidity asset to the withdrawer
        IERC20(liquidityAsset).transfer(msg.sender, assets);

        emit RedeemClaimed(msg.sender, requestId, assets);
    }

    /// @notice Used to work out if an ARM's withdrawal request can be claimed.
    /// If the withdrawal request's `queued` amount is less than or equal to the returned `claimableAmount`, then
    /// the withdrawal request can be claimed.
    /// @return claimableAmount The ARM's already claimed withdrawal requests plus the liquidity in the ARM
    /// and liquidity that is withdrawable from the lending market.
    function claimable() public view returns (uint256 claimableAmount) {
        claimableAmount = withdrawsClaimed + IERC20(liquidityAsset).balanceOf(address(this));

        // if there is an active lending market, add to the claimable amount
        address activeMarketMem = activeMarket;
        if (activeMarketMem != address(0)) {
            // maxWithdraw is used as during periods of high utilization or temporary pauses,
            // maxWithdraw may return less than convertToAssets.
            claimableAmount += IERC4626(activeMarketMem).maxWithdraw(address(this));
        }
    }

    ////////////////////////////////////////////////////
    ///         Asset amount functions
    ////////////////////////////////////////////////////

    /// @dev Checks if there is enough liquidity asset (WETH) in the ARM is not reserved for the withdrawal queue.
    // That is, the amount of liquidity assets (WETH) that is available to be swapped or collected as fees.
    // If no outstanding withdrawals, no check will be done of the amount against the balance of the liquidity assets in the ARM.
    // This is a gas optimization for swaps.
    // The ARM can swap out liquidity assets (WETH) that has been accrued from the performance fee for the fee collector.
    // There is no liquidity guarantee for the fee collector. If there is not enough liquidity assets (WETH) in
    // the ARM to collect the accrued fees, then the fee collector will have to wait until there is enough liquidity assets.
    function _requireLiquidityAvailable(uint256 amount) internal view {
        // The amount of liquidity assets (WETH) that is still to be claimed in the withdrawal queue
        uint256 outstandingWithdrawals = withdrawsQueued - withdrawsClaimed;

        // Save gas on an external balanceOf call if there are no outstanding withdrawals
        if (outstandingWithdrawals == 0) return;

        // If there is not enough liquidity assets in the ARM to cover the outstanding withdrawals and the amount
        require(
            amount + outstandingWithdrawals <= IERC20(liquidityAsset).balanceOf(address(this)),
            "ARM: Insufficient liquidity"
        );
    }

    /// @notice The total amount of assets in the ARM, active lending market and external withdrawal queue,
    /// less the liquidity assets reserved for the ARM's withdrawal queue and accrued fees.
    /// @return The total amount of assets in the ARM
    function totalAssets() public view virtual returns (uint256) {
        (uint256 fees, uint256 newAvailableAssets) = _feesAccrued();

        // total assets should only go up from the initial deposit amount that is burnt
        // but in case of something unforeseen, return at least MIN_TOTAL_SUPPLY.
        if (fees + MIN_TOTAL_SUPPLY >= newAvailableAssets) return MIN_TOTAL_SUPPLY;

        // Remove the performance fee from the available assets
        return newAvailableAssets - fees;
    }

    /// @notice The liquidity asset used for deposits and redeems. eg WETH or wS
    /// Used for compatibility with ERC-4626
    /// @return The address of the liquidity asset
    function asset() external view virtual returns (address) {
        return liquidityAsset;
    }

    /// @dev Calculate the available assets which is the assets in the ARM, external withdrawal queue,
    /// and active lending market, less liquidity assets reserved for the ARM's withdrawal queue.
    /// This does not exclude any accrued performance fees.
    function _availableAssets() internal view returns (uint256 availableAssets, uint256 outstandingWithdrawals) {
        // Convert the base assets in the ARM to the amount of liquidity assets
        uint256 baseConvertedToLiquid = _convert(baseAsset, IERC20(baseAsset).balanceOf(address(this)));

        // Liquidity assets, eg WETH, in the ARM and lending markets are valued at 1.0.
        // Base assets, eg stETH, in the withdrawal queue are valued at the amount of liquidity assets that are expected to be returned.
        // Base assets, eg stETH, in the ARM is converted to liquidity assets and then the cross price applied. The cross price
        // is the discounted price for the redemption time delay. This ensures the ARM's assets per share does not decrease if the ARM
        // sells base assets at a discount (less than 1). That's because the base sell price is greater than or equal to the cross price.
        uint256 assets = IERC20(liquidityAsset).balanceOf(address(this)) + _externalWithdrawQueue()
            + baseConvertedToLiquid * crossPrice / PRICE_SCALE;

        address activeMarketMem = activeMarket;
        if (activeMarketMem != address(0)) {
            // Get all the active lending market shares owned by this ARM contract
            uint256 allShares = IERC4626(activeMarketMem).balanceOf(address(this));
            // Add all the assets in the active lending market.
            // previewRedeem is used instead of maxWithdraw as maxWithdraw will return less if the market
            // is highly utilized or has a temporary pause.
            assets += IERC4626(activeMarketMem).previewRedeem(allShares);
        }

        // The amount of liquidity assets, eg WETH, that is still to be claimed in the withdrawal queue
        outstandingWithdrawals = withdrawsQueued - withdrawsClaimed;

        // If the ARM becomes insolvent enough that the available assets in the ARM and external withdrawal queue
        // is less than the outstanding withdrawals and accrued fees.
        if (assets < outstandingWithdrawals) {
            return (0, outstandingWithdrawals);
        }

        // Need to remove the liquidity assets that have been reserved for the withdrawal queue
        availableAssets = assets - outstandingWithdrawals;
    }

    /// @dev Hook for calculating the amount of liquidity assets in an external withdrawal queue like Lido or OETH.
    /// @return assets The amount of liquidity assets, eg WETH or wS, expected to be returned from the external withdrawal queue.
    /// The actual amount returned can be less in the event of a slashing.
    /// This is not the ARM's withdrawal queue.
    function _externalWithdrawQueue() internal view virtual returns (uint256 assets);

    /// @notice Calculates the amount of shares for a given amount of liquidity assets
    /// @dev Total assets can't be zero. The lowest it can be is MIN_TOTAL_SUPPLY
    /// @param assets The amount of liquidity assets to convert to shares
    /// @return shares The amount of shares that would be minted for the given assets
    function convertToShares(uint256 assets) public view returns (uint256 shares) {
        shares = assets * totalSupply() / totalAssets();
    }

    /// @notice Calculates the amount of liquidity assets for a given amount of shares
    /// @dev Total supply can't be zero. The lowest it can be is MIN_TOTAL_SUPPLY
    /// @param shares The amount of shares to convert to assets
    /// @return assets The amount of liquidity assets that would be received for the given shares
    function convertToAssets(uint256 shares) public view returns (uint256 assets) {
        assets = (shares * totalAssets()) / totalSupply();
    }

    ////////////////////////////////////////////////////
    ///         Performance Fee Functions
    ////////////////////////////////////////////////////

    /// @notice Owner sets the performance fee on increased assets
    /// @param _fee The performance fee measured in basis points (1/100th of a percent)
    /// 10,000 = 100% performance fee
    /// 500 = 5% performance fee
    /// The max allowed performance fee is 50% (5000)
    function setFee(uint256 _fee) external onlyOwner {
        _setFee(_fee);
    }

    /// @notice Owner sets the account/contract that receives the performance fee
    /// @param _feeCollector The address of the fee collector
    function setFeeCollector(address _feeCollector) external onlyOwner {
        _setFeeCollector(_feeCollector);
    }

    function _setFee(uint256 _fee) internal {
        require(_fee <= FEE_SCALE / 2, "ARM: fee too high");

        // Collect any performance fees up to this point using the old fee
        collectFees();

        fee = SafeCast.toUint16(_fee);

        emit FeeUpdated(_fee);
    }

    function _setFeeCollector(address _feeCollector) internal {
        require(_feeCollector != address(0), "ARM: invalid fee collector");

        feeCollector = _feeCollector;

        emit FeeCollectorUpdated(_feeCollector);
    }

    /// @notice Transfer accrued performance fees to the fee collector
    /// This requires enough liquidity assets (WETH) in the ARM that are not reserved
    /// for the withdrawal queue to cover the accrued fees.
    /// @return fees The amount of performance fees collected
    function collectFees() public returns (uint256 fees) {
        uint256 newAvailableAssets;
        // Accrue any performance fees up to this point
        (fees, newAvailableAssets) = _feesAccrued();

        // Save the new available assets back to storage less the collected fees.
        // This needs to be done before the fees == 0 check to cover the scenario where the performance fee is zero
        // and there has been an increase in assets since the last time fees were collected.
        lastAvailableAssets = SafeCast.toInt128(SafeCast.toInt256(newAvailableAssets) - SafeCast.toInt256(fees));

        if (fees == 0) return 0;

        // Check there is enough liquidity assets (WETH) that are not reserved for the withdrawal queue
        // to cover the fee being collected.
        _requireLiquidityAvailable(fees);
        // _requireLiquidityAvailable() is optimized for swaps so will not revert if there are no outstanding withdrawals.
        // We need to check there is enough liquidity assets to cover the fees being collect from this ARM contract.
        // We could try the transfer and let it revert if there are not enough assets, but there is no error message with
        // a failed WETH transfer so we spend the extra gas to check and give a meaningful error message.
        require(fees <= IERC20(liquidityAsset).balanceOf(address(this)), "ARM: insufficient liquidity");

        IERC20(liquidityAsset).transfer(feeCollector, fees);

        emit FeeCollected(feeCollector, fees);
    }

    /// @notice Calculates the performance fees accrued since the last time fees were collected
    /// @param fees The amount of performance fees accrued
    function feesAccrued() external view returns (uint256 fees) {
        (fees,) = _feesAccrued();
    }

    function _feesAccrued() internal view returns (uint256 fees, uint256 newAvailableAssets) {
        (newAvailableAssets,) = _availableAssets();

        // Calculate the increase in assets since the last time fees were calculated
        int256 assetIncrease = SafeCast.toInt256(newAvailableAssets) - lastAvailableAssets;

        // Do not accrued a performance fee if the available assets has decreased
        if (assetIncrease <= 0) return (0, newAvailableAssets);

        fees = SafeCast.toUint256(assetIncrease) * fee / FEE_SCALE;
    }

    ////////////////////////////////////////////////////
    ///         Lending Market Functions
    ////////////////////////////////////////////////////

    /// @notice Owner adds supported lending market to the ARM.
    /// In order to be a safe lending market for the ARM, it must be:
    ///  1. up only exchange rate
    ///  2. no slippage
    ///  3. no fees.
    /// @param _markets The addresses of the lending markets to add
    function addMarkets(address[] calldata _markets) external onlyOwner {
        for (uint256 i = 0; i < _markets.length; ++i) {
            address market = _markets[i];
            require(market != address(0), "ARM: invalid market");
            require(!supportedMarkets[market], "ARM: market already supported");
            require(IERC4626(market).asset() == liquidityAsset, "ARM: invalid market asset");

            supportedMarkets[market] = true;

            emit MarketAdded(market);
        }
    }

    /// @notice Owner removes a supported lending market from the ARM.
    /// This can not be the active market.
    /// @param _market The address of the lending market to remove
    function removeMarket(address _market) external onlyOwner {
        require(_market != address(0), "ARM: invalid market");
        require(supportedMarkets[_market], "ARM: market not supported");
        require(_market != activeMarket, "ARM: market in active");

        supportedMarkets[_market] = false;

        emit MarketRemoved(_market);
    }

    /// @notice set a new active lending market for the ARM.
    /// This can be set to address(0) to disable the use of a lending market.
    /// @param _market The address of the lending market to set as active
    function setActiveMarket(address _market) external onlyOperatorOrOwner {
        require(_market == address(0) || supportedMarkets[_market], "ARM: market not supported");
        // Read once from storage to save gas and make it clear this is the previous active market
        address previousActiveMarket = activeMarket;
        // Don't revert if the previous active market is the same as the new one
        if (previousActiveMarket == _market) return;

        if (previousActiveMarket != address(0)) {
            // Redeem all shares from the previous active lending market.
            // balanceOf is used instead of maxRedeem to ensure all shares are redeemed.
            // maxRedeem can return a smaller amount of shares than balanceOf if the market is highly utilized.
            uint256 shares = IERC4626(previousActiveMarket).balanceOf(address(this));

            if (shares > 0) {
                // This could fail if the market has high utilization. In this case, the Operator needs
                // to wait until the utilization drops before setting a new active market.
                // The redeem can also fail if the ARM has a dust amount of shares left. eg 100 wei.
                // If that happens, the Operator can transfer a tiny amount of active market shares
                // to the ARM so the following redeem will not fail.
                IERC4626(previousActiveMarket).redeem(shares, address(this), address(this));
            }
        }

        activeMarket = _market;

        emit ActiveMarketUpdated(_market);

        // Exit if no new active market
        if (_market == address(0)) return;

        _allocate();
    }

    /// @notice Deposit or withdraw liquidity assets to/from the active lending market
    /// to match the ARM's liquidity buffer which is a percentage of the available assets.
    /// The buffer excludes liquidity assets reserved for the ARM's withdrawal queue. That is, more
    /// liquidity assets will be withdrawn from the lending market if the ARM's liquidity asset balance
    /// does not cover the buffer, which can be zero, and the ARM's outstanding withdrawals.
    /// Will revert if there is no active lending market set.
    /// @return targetLiquidityDelta the desired amount that is deposited/withdrawn to/from the lending market.
    /// A positive value is the liquidity assets that should be deposited to the lending market.
    /// A negative value is the desired liquidity assets that should be withdrawn from the lending market.
    /// @return actualLiquidityDelta the actual amount that is deposited/withdrawn to/from the lending market.
    /// A positive value is the liquidity assets that were deposited to the lending market.
    /// A negative value is the liquidity assets that were withdrawn from the lending market. This can be less than
    /// the `targetLiquidityDelta`, or even zero, if there is high utilization in the lending market.
    function allocate() external returns (int256 targetLiquidityDelta, int256 actualLiquidityDelta) {
        require(activeMarket != address(0), "ARM: no active market");

        return _allocate();
    }

    function _allocate() internal returns (int256 targetLiquidityDelta, int256 actualLiquidityDelta) {
        (uint256 availableAssets, uint256 outstandingWithdrawals) = _availableAssets();
        if (availableAssets == 0) return (0, 0);
        uint256 targetArmLiquidity = availableAssets * armBuffer / 1e18;

        // The current liquidity available in swap is the liquidity asset balance less
        // any outstanding withdrawals from the ARM's withdrawal queue
        int256 currentArmLiquidity = SafeCast.toInt256(IERC20(liquidityAsset).balanceOf(address(this)))
            - SafeCast.toInt256(outstandingWithdrawals);

        targetLiquidityDelta = currentArmLiquidity - SafeCast.toInt256(targetArmLiquidity);

        // Load the active lending market address from storage to save gas
        address activeMarketMem = activeMarket;

        // The allocateThreshold prevents the ARM from constantly depositing and withdrawing if there are rounding issues
        if (targetLiquidityDelta > allocateThreshold) {
            // We have too much liquidity in the ARM, we need to deposit some to the active lending market

            uint256 depositAmount = SafeCast.toUint256(targetLiquidityDelta);

            IERC20(liquidityAsset).approve(activeMarketMem, depositAmount);
            IERC4626(activeMarketMem).deposit(depositAmount, address(this));

            actualLiquidityDelta = SafeCast.toInt256(depositAmount);
        } else if (targetLiquidityDelta < 0) {
            // We have too little liquidity in the ARM, we need ...

// [truncated — 52541 bytes total]
Interfaces.sol 366 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function decimals() external view returns (uint8);

    event Transfer(address indexed from, address indexed to, uint256 value);
}

interface IOethARM {
    function token0() external returns (address);
    function token1() external returns (address);
    function owner() external returns (address);

    /**
     * @notice Swaps an exact amount of input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param to Recipient of the output tokens.
     * @return amounts The input and output token amounts.
     */
    function swapExactTokensForTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Uniswap V2 Router compatible interface. Swaps an exact amount of
     * input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Receive an exact amount of output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param to Recipient of the output tokens.
     * @return amounts The input and output token amounts.
     */
    function swapTokensForExactTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountOut,
        uint256 amountInMax,
        address to
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Uniswap V2 Router compatible interface. Receive an exact amount of
     * output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function setOwner(address newOwner) external;
    function transferToken(address token, address to, uint256 amount) external;

    // From OethLiquidityManager
    function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);
    function claimWithdrawal(uint256 requestId) external;
    function claimWithdrawals(uint256[] calldata requestIds) external;
}

interface ILiquidityProviderARM is IERC20 {
    function previewDeposit(uint256 assets) external returns (uint256 shares);
    function deposit(uint256 assets) external returns (uint256 shares);
    function deposit(uint256 assets, address liquidityProvider) external returns (uint256 shares);

    function previewRedeem(uint256 shares) external returns (uint256 assets);
    function requestRedeem(uint256 shares) external returns (uint256 requestId, uint256 assets);
    function claimRedeem(uint256 requestId) external returns (uint256 assets);

    function totalAssets() external returns (uint256 assets);
    function convertToShares(uint256 assets) external returns (uint256 shares);
    function convertToAssets(uint256 shares) external returns (uint256 assets);
    function lastTotalAssets() external returns (uint256 assets);
}

interface ICapManager {
    function postDepositHook(address liquidityProvider, uint256 assets) external;
}

interface LegacyAMM {
    function transferToken(address tokenOut, address to, uint256 amount) external;
}

interface IOriginVault {
    function mint(address _asset, uint256 _amount, uint256 _minimumOusdAmount) external;

    function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;

    function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);

    function claimWithdrawal(uint256 requestId) external returns (uint256 amount);

    function claimWithdrawals(uint256[] memory requestIds)
        external
        returns (uint256[] memory amounts, uint256 totalAmount);

    function addWithdrawalQueueLiquidity() external;

    function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;

    function governor() external view returns (address);

    function dripper() external view returns (address);

    function withdrawalQueueMetadata()
        external
        view
        returns (uint128 queued, uint128 claimable, uint128 claimed, uint128 nextWithdrawalIndex);

    function withdrawalRequests(uint256 requestId)
        external
        view
        returns (address withdrawer, bool claimed, uint40 timestamp, uint128 amount, uint128 queued);

    function withdrawalClaimDelay() external view returns (uint256);
}

interface IGovernance {
    enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Queued,
        Expired,
        Executed
    }

    function state(uint256 proposalId) external view returns (ProposalState);

    function proposalSnapshot(uint256 proposalId) external view returns (uint256);

    function proposalDeadline(uint256 proposalId) external view returns (uint256);

    function proposalEta(uint256 proposalId) external view returns (uint256);

    function votingDelay() external view returns (uint256);

    function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);

    function queue(uint256 proposalId) external;

    function execute(uint256 proposalId) external;
}

interface IWETH is IERC20 {
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    function deposit() external payable;
    function withdraw(uint256 wad) external;
}

interface ISTETH is IERC20 {
    event Submitted(address indexed sender, uint256 amount, address referral);

    // function() external payable;
    function submit(address _referral) external payable returns (uint256);
}

interface IStETHWithdrawal {
    event WithdrawalRequested(
        uint256 indexed requestId,
        address indexed requestor,
        address indexed owner,
        uint256 amountOfStETH,
        uint256 amountOfShares
    );
    event WithdrawalsFinalized(
        uint256 indexed from, uint256 indexed to, uint256 amountOfETHLocked, uint256 sharesToBurn, uint256 timestamp
    );
    event WithdrawalClaimed(
        uint256 indexed requestId, address indexed owner, address indexed receiver, uint256 amountOfETH
    );

    struct WithdrawalRequestStatus {
        /// @notice stETH token amount that was locked on withdrawal queue for this request
        uint256 amountOfStETH;
        /// @notice amount of stETH shares locked on withdrawal queue for this request
        uint256 amountOfShares;
        /// @notice address that can claim or transfer this request
        address owner;
        /// @notice timestamp of when the request was created, in seconds
        uint256 timestamp;
        /// @notice true, if request is finalized
        bool isFinalized;
        /// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed)
        bool isClaimed;
    }

    function transferFrom(address _from, address _to, uint256 _requestId) external;
    function ownerOf(uint256 _requestId) external returns (address);
    function requestWithdrawals(uint256[] calldata _amounts, address _owner)
        external
        returns (uint256[] memory requestIds);
    function getLastCheckpointIndex() external view returns (uint256);
    function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex)
        external
        view
        returns (uint256[] memory hintIds);
    function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;
    function getWithdrawalStatus(uint256[] calldata _requestIds)
        external
        view
        returns (WithdrawalRequestStatus[] memory statuses);
    function getWithdrawalRequests(address _owner) external view returns (uint256[] memory requestsIds);
    function getLastRequestId() external view returns (uint256);
}

interface IOracle {
    function price(address asset) external view returns (uint256 price);
}

interface IHarvestable {
    function collectRewards() external returns (address[] memory tokens, uint256[] memory rewards);
}

interface IMagpieRouter {
    function swapWithMagpieSignature(bytes calldata) external payable returns (uint256 amountOut);
}

library DistributionTypes {
    struct IncentivesProgramCreationInput {
        string name;
        address rewardToken;
        uint104 emissionPerSecond;
        uint40 distributionEnd;
    }
}

library IDistributionManager {
    struct AccruedRewards {
        uint256 amount;
        bytes32 programId;
        address rewardToken;
    }
}

interface SiloIncentivesControllerGaugeLike {
    function claimRewards(address _to) external returns (IDistributionManager.AccruedRewards[] memory accruedRewards);
    function createIncentivesProgram(DistributionTypes.IncentivesProgramCreationInput memory _incentivesProgramInput)
        external;
    function getAllProgramsNames() external view returns (string[] memory programsNames);
    function getRewardsBalance(address _user, string memory _programName)
        external
        view
        returns (uint256 unclaimedRewards);
    function incentivesPrograms(bytes32)
        external
        view
        returns (
            uint256 index,
            address rewardToken,
            uint104 emissionPerSecond,
            uint40 lastUpdateTimestamp,
            uint40 distributionEnd
        );
    function owner() external view returns (address);
}

interface IEETHWithdrawal {
    function requestWithdraw(address receipient, uint256 amount) external returns (uint256 requestId);
}

interface IEETHWithdrawalNFT {
    function finalizeRequests(uint256 requestId) external;
    function claimWithdraw(uint256 requestId) external;
    function batchClaimWithdraw(uint256[] calldata requestIds) external;
}

interface IEETHRedemptionManager {
    function redeemWeEth(uint256 amount, address receiver) external;
    function canRedeem(uint256 amount) external view returns (bool);
}

interface IDistributor {
    function claim(
        address[] calldata users,
        address[] calldata tokens,
        uint256[] calldata amounts,
        bytes32[][] calldata proofs
    ) external;
}

// Ethena Interfaces

struct UserCooldown {
    uint104 cooldownEnd;
    uint152 underlyingAmount;
}

interface IStakedUSDe is IERC4626 {
    // Errors //
    /// @notice Error emitted when the shares amount to redeem is greater than the shares balance of the owner
    error ExcessiveRedeemAmount();
    /// @notice Error emitted when the shares amount to withdraw is greater than the shares balance of the owner
    error ExcessiveWithdrawAmount();

    function cooldownAssets(uint256 assets) external returns (uint256 shares);

    function cooldownShares(uint256 shares) external returns (uint256 assets);

    function unstake(address receiver) external;

    function cooldowns(address receiver) external view returns (UserCooldown memory);

    function getUnvestedAmount() external view returns (uint256);

    function lastDistributionTimestamp() external view returns (uint256);

    function transferInRewards(uint256 amount) external;
}
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);
            }
        }
    }
}
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);
}
OwnableOperable.sol 38 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Ownable} from "./Ownable.sol";

/**
 * @title Base contract that provides ownership and operational control
 * @author Origin Protocol Inc
 */
contract OwnableOperable is Ownable {
    /// @notice The account that can request and claim withdrawals.
    address public operator;

    uint256[49] private _gap;

    event OperatorChanged(address newAdmin);

    function _initOwnableOperable(address _operator) internal {
        _setOperator(_operator);
    }

    /// @notice Set the account that can request and claim withdrawals.
    /// @param newOperator The address of the new operator.
    function setOperator(address newOperator) external onlyOwner {
        _setOperator(newOperator);
    }

    function _setOperator(address newOperator) internal {
        operator = newOperator;

        emit OperatorChanged(newOperator);
    }

    modifier onlyOperatorOrOwner() {
        require(msg.sender == operator || msg.sender == _owner(), "ARM: Only operator or owner can call this function.");
        _;
    }
}
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);
}
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);
}
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;
    }
}
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);
}
Ownable.sol 57 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**
 * @title Base contract that provides ownership control
 * @author Origin Protocol Inc
 */
contract Ownable {
    /// @notice The slot used to store the owner of the contract.
    /// This is also used as the proxy admin.
    /// keccak256(“eip1967.proxy.admin”) - 1 per EIP 1967
    bytes32 internal constant OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    event AdminChanged(address previousAdmin, address newAdmin);

    constructor() {
        assert(OWNER_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _setOwner(msg.sender);
    }

    /// @notice The contract owner and proxy admin.
    function owner() public view returns (address) {
        return _owner();
    }

    /// @notice Set the owner and proxy admin of the contract.
    /// @param newOwner The address of the new owner.
    function setOwner(address newOwner) external onlyOwner {
        _setOwner(newOwner);
    }

    function _owner() internal view returns (address ownerOut) {
        bytes32 position = OWNER_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            ownerOut := sload(position)
        }
    }

    function _setOwner(address newOwner) internal {
        emit AdminChanged(_owner(), newOwner);
        bytes32 position = OWNER_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(position, newOwner)
        }
    }

    function _onlyOwner() internal view {
        require(msg.sender == _owner(), "ARM: Only owner can call this function.");
    }

    modifier onlyOwner() {
        _onlyOwner();
        _;
    }
}

Read Contract

FEE_SCALE 0x8a5fddd8 → uint256
MAX_CROSS_PRICE_DEVIATION 0x090b78c5 → uint256
PRICE_SCALE 0xc33f59d3 → uint256
activeMarket 0xce318c51 → address
allocateThreshold 0x4a8ff603 → int256
allowance 0xdd62ed3e → uint256
armBuffer 0xa4c84f25 → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
baseAsset 0xcdf456e1 → address
capManager 0x6d785a87 → address
claimDelay 0x1c8ec299 → uint256
claimable 0xaf38d757 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
crossPrice 0xf5488330 → uint256
decimals 0x313ce567 → uint8
fee 0xddca3f43 → uint16
feeCollector 0xc415b95c → address
feesAccrued 0x94db0595 → uint256
getReserves 0x0902f1ac → uint256, uint256
lastAvailableAssets 0x2eb6328b → int128
lidoWithdrawalQueue 0xff3368a1 → address
lidoWithdrawalQueueAmount 0x31ca1c02 → uint256
lidoWithdrawalRequests 0x7d8187f9 → uint256
liquidityAsset 0x209b2bca → address
minSharesToRedeem 0x50d0ea39 → uint256
name 0x06fdde03 → string
nextWithdrawalIndex 0xbba9282e → uint256
operator 0x570ca735 → address
owner 0x8da5cb5b → address
previewDeposit 0xef8b30f7 → uint256
previewRedeem 0x4cdad506 → uint256
steth 0x953d7ee2 → address
supportedMarkets 0x20761fc4 → bool
symbol 0x95d89b41 → string
token0 0x0dfe1681 → address
token1 0xd21220a7 → address
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256
traderate0 0x45059a6b → uint256
traderate1 0xcf1de5d8 → uint256
weth 0x3fc8cef3 → address
withdrawalRequests 0x937b2581 → address, bool, uint40, uint128, uint128, uint128
withdrawsClaimed 0x35ce81c4 → uint128
withdrawsQueued 0x6ec68625 → uint128

Write Contract 28 functions

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

addMarkets 0xda40385d
address[] _markets
allocate 0xabaa9916
No parameters
returns: int256, int256
approve 0x095ea7b3
address spender
uint256 value
returns: bool
claimLidoWithdrawals 0xd04bf024
uint256[] requestIds
uint256[] hintIds
claimRedeem 0xe46cf747
uint256 requestId
returns: uint256
collectFees 0xc8796572
No parameters
returns: uint256
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
deposit 0xb6b55f25
uint256 assets
returns: uint256
initialize 0xb3ddda2a
string _name
string _symbol
address _operator
uint256 _fee
address _feeCollector
address _capManager
registerLidoWithdrawalRequests 0xd5a10065
No parameters
removeMarket 0xdb913236
address _market
requestLidoWithdrawals 0x674eb980
uint256[] amounts
returns: uint256[]
requestRedeem 0xaa2f892d
uint256 shares
returns: uint256, uint256
setARMBuffer 0x95f9e9e6
uint256 _armBuffer
setActiveMarket 0xab710b24
address _market
setCapManager 0x0e608b30
address _capManager
setCrossPrice 0x30486f3c
uint256 newCrossPrice
setFee 0x69fe0e2d
uint256 _fee
setFeeCollector 0xa42dce80
address _feeCollector
setOperator 0xb3ab15fb
address newOperator
setOwner 0x13af4035
address newOwner
setPrices 0x05fefda7
uint256 buyT1
uint256 sellT1
swapExactTokensForTokens 0x38ed1739
uint256 amountIn
uint256 amountOutMin
address[] path
address to
uint256 deadline
returns: uint256[]
swapExactTokensForTokens 0x6c08c57e
address inToken
address outToken
uint256 amountIn
uint256 amountOutMin
address to
returns: uint256[]
swapTokensForExactTokens 0x8803dbee
uint256 amountOut
uint256 amountInMax
address[] path
address to
uint256 deadline
returns: uint256[]
swapTokensForExactTokens 0xf7d31809
address inToken
address outToken
uint256 amountOut
uint256 amountInMax
address to
returns: uint256[]
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool

Recent Transactions

No transactions found for this address