Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x70dAf8B0BFc846cc98b71D2F8FfdC91f4D2bbd51
Balance 0 ETH
Nonce 1
Code Size 11070 bytes
Indexed Transactions 0 (1 on-chain, 0.8% indexed)
External Etherscan · Sourcify

Contract Bytecode

11070 bytes
0x608060405260043610610186575f3560e01c80636a4c410d116100d1578063cc1698221161007c578063f1650a4611610057578063f1650a46146104cf578063f3cbc0f5146104ed578063fb237eb21461050c575f5ffd5b8063cc16982214610463578063e00a130d14610491578063e1bce57a146104b0575f5ffd5b8063a881cda2116100ac578063a881cda214610406578063c1fe3e4814610425578063c4d66de814610444575f5ffd5b80636a4c410d146103945780637a8dce35146103a957806381889321146103e7575f5ffd5b80632f0d3d8311610131578063458bfa011161010c578063458bfa011461032757806349bdaa15146103605780635e7a000f14610375575f5ffd5b80632f0d3d83146102c057806337d5fe99146102df5780633ea29f6c146102fe575f5ffd5b806315b819791161016157806315b819791461024b57806318a669531461026a57806323f87d01146102a1575f5ffd5b8063056b56cd146101915780630dd4433d146101b2578063150b7a02146101d1575f5ffd5b3661018d57005b5f5ffd5b34801561019c575f5ffd5b506101b06101ab36600461262d565b610543565b005b3480156101bd575f5ffd5b506101b06101cc366004612657565b610922565b3480156101dc575f5ffd5b506102156101eb36600461266e565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b348015610256575f5ffd5b506101b0610265366004612714565b6109f4565b348015610275575f5ffd5b50603454610289906001600160a01b031681565b6040516001600160a01b039091168152602001610242565b3480156102ac575f5ffd5b506101b06102bb366004612657565b610b8e565b3480156102cb575f5ffd5b506101b06102da366004612796565b610d18565b3480156102ea575f5ffd5b50603554610289906001600160a01b031681565b348015610309575f5ffd5b50610312610dec565b60408051928352602083019190915201610242565b348015610332575f5ffd5b506103526103413660046127b6565b60396020525f908152604090205481565b604051908152602001610242565b34801561036b575f5ffd5b50610352603c5481565b348015610380575f5ffd5b506101b061038f366004612657565b610dfe565b34801561039f575f5ffd5b50610352603a5481565b3480156103b4575f5ffd5b506103d76103c33660046127b6565b60386020525f908152604090205460ff1681565b6040519015158152602001610242565b3480156103f2575f5ffd5b506101b06104013660046127d8565b610ec5565b348015610411575f5ffd5b506101b061042036600461262d565b610fe8565b348015610430575f5ffd5b50603654610289906001600160a01b031681565b34801561044f575f5ffd5b506101b061045e3660046127b6565b6113ba565b34801561046e575f5ffd5b506103d761047d366004612657565b60376020525f908152604090205460ff1681565b34801561049c575f5ffd5b506101b06104ab366004612657565b61156a565b3480156104bb575f5ffd5b50603354610289906001600160a01b031681565b3480156104da575f5ffd5b505f54610289906001600160a01b031681565b3480156104f8575f5ffd5b506101b061050736600461280f565b611699565b348015610517575f5ffd5b506103d76105263660046127b6565b6001600160a01b03165f908152603b602052604090205460ff1690565b5f546040517f9be918e60000000000000000000000000000000000000000000000000000000081526001600160a01b03808516600483015284921690639be918e690602401602060405180830381865afa1580156105a3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c79190612868565b6105fd576040517f981a2a2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b0382160161065f576040517f4b1f787a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54604051632474521560e21b81527f9c20764ee489aca9767eb49ca1ea4b9367182d11ec9bd87ff1dafc16a7009d4760048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa1580156106ca573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106ee9190612868565b610724576040517f0ddb531200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8054604051631c2d8fb360e31b81527f7a8fe1bac8d7638862c53b62ffada56d0a56c381287c35f66503b5b86fa88b8560048201526001600160a01b039091169063e16c7d9890602401602060405180830381865afa15801561078a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ae9190612883565b5f8054604051631c2d8fb360e31b81527f0900d19e2faab4e79535bcc1cfdb63996d43c8e38d9a260cf2b01e820b5f84d4600482015292935090916001600160a01b039091169063e16c7d9890602401602060405180830381865afa158015610819573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083d9190612883565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b03878116600483015291925082915f91670de0b6b3a76400009184169063b3596f0790602401602060405180830381865afa1580156108ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108cf919061289e565b6108d990886128c9565b6108e391906128e6565b905080603a54116108f4575f610902565b80603a546109029190612905565b603a556109196001600160a01b038816858861187e565b50505050505050565b61092a61192c565b5f54604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92960048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610995573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b99190612868565b6109d657604051632e8726f760e11b815260040160405180910390fd5b6109df81611985565b6109e8476119fa565b6109f160018055565b50565b5f54604051632474521560e21b81527faf290d8680820aad922855f39b306097b20e28774d6c1ad35a20325630c3a02c60048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610a5f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a839190612868565b610ab9576040517f210d9c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b82811015610b885781603b5f868685818110610ad957610ad9612918565b9050602002016020810190610aee91906127b6565b6001600160a01b0316815260208101919091526040015f20805460ff1916911515919091179055838382818110610b2757610b27612918565b9050602002016020810190610b3c91906127b6565b6001600160a01b03167f995d2434d36c9bceaab42c56efe51e8ffe41fd11cabefd9e2cb83700d8b2035e83604051610b78911515815260200190565b60405180910390a2600101610abb565b50505050565b610b9661192c565b5f54604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92960048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610c01573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c259190612868565b610c4257604051632e8726f760e11b815260040160405180910390fd5b80805f03610c7c576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610c85611b48565b905080603c54610c95919061292c565b821115610cce576040517f86d4fca100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c5415610d04575f603c548311610ce65782610cea565b603c545b905080603c5f828254610cfd9190612905565b9091555050505b610d0d83611c6e565b50506109f160018055565b610d2061192c565b5f54604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92960048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610d8b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610daf9190612868565b610dcc57604051632e8726f760e11b815260040160405180910390fd5b610dd68282611db0565b610ddf476119fa565b610de860018055565b5050565b603c545f610df8611b48565b90509091565b610e0661192c565b5f54604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92960048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610e71573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e959190612868565b610eb257604051632e8726f760e11b815260040160405180910390fd5b610ebb81611eb0565b506109f160018055565b5f54604051632474521560e21b81527faf290d8680820aad922855f39b306097b20e28774d6c1ad35a20325630c3a02c60048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa158015610f30573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f549190612868565b610f8a576040517f210d9c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0382165f818152603b6020908152604091829020805460ff191685151590811790915591519182527f995d2434d36c9bceaab42c56efe51e8ffe41fd11cabefd9e2cb83700d8b2035e910160405180910390a25050565b5f546040517f9be918e60000000000000000000000000000000000000000000000000000000081526001600160a01b03808516600483015284921690639be918e690602401602060405180830381865afa158015611048573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106c9190612868565b6110a2576040517f981a2a2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b03821601611104576040517f4b1f787a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54604051632474521560e21b81527f9c20764ee489aca9767eb49ca1ea4b9367182d11ec9bd87ff1dafc16a7009d4760048201523360248201526001600160a01b03909116906391d1485490604401602060405180830381865afa15801561116f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111939190612868565b6111c9576040517f0ddb531200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8054604051631c2d8fb360e31b81527f7a8fe1bac8d7638862c53b62ffada56d0a56c381287c35f66503b5b86fa88b8560048201526001600160a01b039091169063e16c7d9890602401602060405180830381865afa15801561122f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112539190612883565b5f8054604051631c2d8fb360e31b81527f0900d19e2faab4e79535bcc1cfdb63996d43c8e38d9a260cf2b01e820b5f84d4600482015292935090916001600160a01b039091169063e16c7d9890602401602060405180830381865afa1580156112be573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112e29190612883565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b0387811660048301529192508291670de0b6b3a7640000919083169063b3596f0790602401602060405180830381865afa15801561134f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611373919061289e565b61137d90876128c9565b61138791906128e6565b603a5f828254611397919061292c565b909155506113b290506001600160a01b038716843088611ff5565b505050505050565b5f54600160a81b900460ff16158080156113e057505f546001600160a01b90910460ff16105b806114005750303b15801561140057505f54600160a01b900460ff166001145b6114775760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b5f80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b17905580156114bd575f805460ff60a81b1916600160a81b1790555b6114c682612046565b6114ce612086565b5f805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038416908117825560405190917f9cf19cefd9aab739c33b95716ee3f3f921f219dc6d7aae25e1f9497b3788915091a28015610de8575f805460ff60a81b19169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b61157261192c565b335f908152603b602052604090205460ff166115ba576040517f2ba75b2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f036115f3576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603c546b033b2e3c9fd0803ce800000090819061161190849061292c565b1115611649576040517f5c0547a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81603c54611657919061292c565b603c5560405182815233907fa36e002c92e491a9411aa72713848cc138ea6a18fdea3168a62dbcb83d6e09279060200160405180910390a2506109f160018055565b5f54600290600160a81b900460ff161580156116c257505f5460ff808316600160a01b90920416105b6117345760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161146e565b5f8054600160a81b7fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff909116600160a01b60ff85160260ff60a81b19161717808255604051632474521560e21b815260048101929092523360248301526001600160a01b0316906391d1485490604401602060405180830381865afa1580156117bf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117e39190612868565b611819576040517f164931f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611821612086565b61182b83836120fc565b61183585856121a3565b5f805460ff60a81b1916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15050505050565b6040516001600160a01b0383166024820152604481018290526119279084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261224a565b505050565b60026001540361197e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161146e565b6002600155565b6033546040517f5e15c749000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0390911690635e15c749906024015f604051808303815f87803b1580156119e1575f5ffd5b505af11580156119f3573d5f5f3e3d5ffd5b5050505050565b5f8054604051631c2d8fb360e31b81527f7a8fe1bac8d7638862c53b62ffada56d0a56c381287c35f66503b5b86fa88b8560048201526001600160a01b039091169063e16c7d9890602401602060405180830381865afa158015611a60573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a849190612883565b905081603a541115611aac5781603a5f828254611aa19190612905565b90915550611ab19050565b5f603a555b806001600160a01b031663e356edb4836040518263ffffffff1660e01b81526004015f604051808303818588803b158015611aea575f5ffd5b505af1158015611afc573d5f5f3e3d5ffd5b5050604080516001600160a01b0386168152602081018790527fcec1f18c3ab8ddaaa107a1591e3c369667eec613626611a8deaedef43069fcdd945001915061155e9050565b60018055565b5f8054604051631c2d8fb360e31b81527f1ffe5e31b761569011f2c9443332f6f987c9e37f8311474a33f44970a97038a0600482015282916001600160a01b03169063e16c7d9890602401602060405180830381865afa158015611bae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bd29190612883565b6040517fb6cf3fef00000000000000000000000000000000000000000000000000000000815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60048201529091506001600160a01b0382169063b6cf3fef90602401602060405180830381865afa158015611c44573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c68919061289e565b91505090565b603554603654611c8b916001600160a01b03918216911683612330565b6040805160018082528183019092525f916020808301908036833701905050905081815f81518110611cbf57611cbf612918565b60209081029190910101526035546040517fd66810420000000000000000000000000000000000000000000000000000000081525f916001600160a01b03169063d668104290611d15908590309060040161298d565b5f604051808303815f875af1158015611d30573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611d5791908101906129b7565b90507f8d29c3cc4ecd5557acf0513e125eccf06f4c6f9f5ec12c5baa5ba7eac2a9458e815f81518110611d8c57611d8c612918565b6020026020010151604051611da391815260200190565b60405180910390a1505050565b6040805160018082528183019092525f916020808301908036833750506040805160018082528183019092529293505f9291506020808301908036833701905050905083825f81518110611e0657611e06612918565b60200260200101818152505082815f81518110611e2557611e25612918565b60209081029190910101526035546040517f5e7eead90000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690635e7eead990611e7d90859085903090600401612a7f565b5f604051808303815f87803b158015611e94575f5ffd5b505af1158015611ea6573d5f5f3e3d5ffd5b5050505050505050565b6033546034545f91611ecf916001600160a01b03908116911684612330565b6033546040517f74dc9d1a000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b03909116906374dc9d1a906024015f604051808303815f87803b158015611f2b575f5ffd5b505af1158015611f3d573d5f5f3e3d5ffd5b5050505060335f9054906101000a90046001600160a01b03166001600160a01b031663061a499f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f91573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fb5919061289e565b90507f5d5fdc94abc691d6fb4e699a6aacafbc8c83a13bdfbd183a271e1d1f1c5ac02581604051611fe891815260200190565b60405180910390a1919050565b6040516001600160a01b0380851660248301528316604482015260648101829052610b889085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016118c3565b6001600160a01b0381166109f1576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f54600160a81b900460ff166120f25760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161146e565b6120fa61240c565b565b5f54600160a81b900460ff166121685760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161146e565b603380546001600160a01b0393841673ffffffffffffffffffffffffffffffffffffffff199182161790915560348054929093169116179055565b5f54600160a81b900460ff1661220f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161146e565b603580546001600160a01b0393841673ffffffffffffffffffffffffffffffffffffffff199182161790915560368054929093169116179055565b5f61229e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124789092919063ffffffff16565b905080515f14806122be5750808060200190518101906122be9190612868565b6119275760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161146e565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015612396573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123ba919061289e565b9050610b88847f095ea7b300000000000000000000000000000000000000000000000000000000856123ec868661292c565b6040516001600160a01b03909216602483015260448201526064016118c3565b5f54600160a81b900460ff16611b425760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161146e565b606061248684845f8561248e565b949350505050565b6060824710156125065760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161146e565b5f5f866001600160a01b031685876040516125219190612abd565b5f6040518083038185875af1925050503d805f811461255b576040519150601f19603f3d011682016040523d82523d5f602084013e612560565b606091505b50915091506125718783838761257c565b979650505050505050565b606083156125ea5782515f036125e3576001600160a01b0385163b6125e35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161146e565b5081612486565b61248683838151156125ff5781518083602001fd5b8060405162461bcd60e51b815260040161146e9190612ad3565b6001600160a01b03811681146109f1575f5ffd5b5f5f6040838503121561263e575f5ffd5b823561264981612619565b946020939093013593505050565b5f60208284031215612667575f5ffd5b5035919050565b5f5f5f5f5f60808688031215612682575f5ffd5b853561268d81612619565b9450602086013561269d81612619565b935060408601359250606086013567ffffffffffffffff8111156126bf575f5ffd5b8601601f810188136126cf575f5ffd5b803567ffffffffffffffff8111156126e5575f5ffd5b8860208284010111156126f6575f5ffd5b959894975092955050506020019190565b80151581146109f1575f5ffd5b5f5f5f60408486031215612726575f5ffd5b833567ffffffffffffffff81111561273c575f5ffd5b8401601f8101861361274c575f5ffd5b803567ffffffffffffffff811115612762575f5ffd5b8660208260051b8401011115612776575f5ffd5b60209182019450925084013561278b81612707565b809150509250925092565b5f5f604083850312156127a7575f5ffd5b50508035926020909101359150565b5f602082840312156127c6575f5ffd5b81356127d181612619565b9392505050565b5f5f604083850312156127e9575f5ffd5b82356127f481612619565b9150602083013561280481612707565b809150509250929050565b5f5f5f5f60808587031215612822575f5ffd5b843561282d81612619565b9350602085013561283d81612619565b9250604085013561284d81612619565b9150606085013561285d81612619565b939692955090935050565b5f60208284031215612878575f5ffd5b81516127d181612707565b5f60208284031215612893575f5ffd5b81516127d181612619565b5f602082840312156128ae575f5ffd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176128e0576128e06128b5565b92915050565b5f8261290057634e487b7160e01b5f52601260045260245ffd5b500490565b818103818111156128e0576128e06128b5565b634e487b7160e01b5f52603260045260245ffd5b808201808211156128e0576128e06128b5565b634e487b7160e01b5f52604160045260245ffd5b5f8151808452602084019350602083015f5b82811015612983578151865260209586019590910190600101612965565b5093949350505050565b604081525f61299f6040830185612953565b90506001600160a01b03831660208301529392505050565b5f602082840312156129c7575f5ffd5b815167ffffffffffffffff8111156129dd575f5ffd5b8201601f810184136129ed575f5ffd5b805167ffffffffffffffff811115612a0757612a0761293f565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715612a3457612a3461293f565b604052918252602081840181019290810187841115612a51575f5ffd5b6020850194505b83851015612a7457845180825260209586019590935001612a58565b509695505050505050565b606081525f612a916060830186612953565b8281036020840152612aa38186612953565b9150506001600160a01b0383166040830152949350505050565b5f82518060208501845e5f920191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f8301168401019150509291505056fea26469706673582212201a4a790070b4865e7d9acc209a246ed116ebb8ca5c3c1ff91f128c5f20a3af3864736f6c634300081b0033

Verified Source Code Full Match

Compiler: v0.8.27+commit.40a35a09 EVM: cancun Optimization: Yes (2000 runs)
Initializable.sol 166 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @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 Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 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 functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _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 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _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() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @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 {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

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

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
ReentrancyGuardUpgradeable.sol 89 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

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

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

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

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

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

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

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
AddressUpgradeable.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IAccessControl.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
IERC721Receiver.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
LRTConverter.sol 271 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

import { LRTConstants } from "./utils/LRTConstants.sol";

import { UtilLib } from "./utils/UtilLib.sol";
import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol";

import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol";
import { ILRTOracle } from "./interfaces/ILRTOracle.sol";
import { ILRTConverter } from "./interfaces/ILRTConverter.sol";
import { ILRTWithdrawalManager } from "./interfaces/ILRTWithdrawalManager.sol";

import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { UnstakeStETH } from "./unstaking-adapters/UnstakeStETH.sol";

/// @dev Legacy unstaking adapters for swETH are kept for upgrade compatibility
import { UnstakeSwETH } from "./unstaking-adapters/UnstakeSwETH.sol";

/// @title LRTConverter - Unstakes LSTs to ETH and swaps ETH to LSTs
/// @notice This contract is responsible for unstaking LSTs to ETH and swapping ETH to LSTs
contract LRTConverter is
    ILRTConverter,
    LRTConfigRoleChecker,
    ReentrancyGuardUpgradeable,
    UnstakeSwETH,
    UnstakeStETH,
    IERC721Receiver
{
    using SafeERC20 for IERC20;

    mapping(bytes32 => bool) public _legacyProcessedWithdrawalRoots;
    mapping(address => bool) public _legacyConvertibleAssets;
    mapping(address => uint256) public _legacyConversionLimit;

    // needs to be added to total assets in protocol
    uint256 public ethValueInWithdrawal;

    mapping(address => bool) private whitelistedUsers;

    uint256 public whitelistedUnstakeAllowance;

    modifier onlyWhitelistedUser() {
        if (!isUserWhitelisted(msg.sender)) {
            revert UserNotWhitelisted();
        }
        _;
    }

    /// @dev Modifier to enforce unstaking limits and update counters
    /// @param amountToUnstake Amount of stETH to unstake
    modifier withinUnstakeLimits(uint256 amountToUnstake) {
        if (amountToUnstake == 0) {
            revert InvalidAmount();
        }

        uint256 availableActiveETHWithdrawals = _getActiveETHUserWithdrawals();

        if (amountToUnstake > whitelistedUnstakeAllowance + availableActiveETHWithdrawals) {
            revert UnstakeLimitExceeded();
        }

        // Consume intended withdrawal limit
        if (whitelistedUnstakeAllowance > 0) {
            uint256 whitelistedAmountConsumed =
                amountToUnstake > whitelistedUnstakeAllowance ? whitelistedUnstakeAllowance : amountToUnstake;

            whitelistedUnstakeAllowance -= whitelistedAmountConsumed;
        }
        _;
    }

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

    /// @dev Initializes the contract
    /// @param lrtConfigAddr LRT config address
    function initialize(address lrtConfigAddr) external initializer {
        UtilLib.checkNonZeroAddress(lrtConfigAddr);
        __ReentrancyGuard_init();
        lrtConfig = ILRTConfig(lrtConfigAddr);
        emit UpdatedLRTConfig(lrtConfigAddr);
    }

    /// @dev Initializes the contract
    /// @param _withdrawalQueueAddress Address of withdrawal queue (stETH)
    /// @param _stETHAddress Address of stETH
    /// @param _swEXITAddress Address of swEXIT (swETH)
    /// @param _swETHAddress Address of swETH
    function initialize2(
        address _withdrawalQueueAddress,
        address _stETHAddress,
        address _swEXITAddress,
        address _swETHAddress
    )
        external
        reinitializer(2)
        onlyLRTAdmin
    {
        __ReentrancyGuard_init();
        __initializeSwETH(_swEXITAddress, _swETHAddress);
        __initializeStETH(_withdrawalQueueAddress, _stETHAddress);
    }

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /// @dev fallback to receive funds
    receive() external payable { }

    /*////////////////////////////////////////////////////////////
                        write interactions
    //////////////////////////////////////////////////////////////*/

    /// @notice send asset from deposit pool to LRTConverter
    /// @dev Only callable by Asset Transfer Role and asset needs to be approved
    /// @param _asset Asset address to send
    /// @param _amount Asset amount to send
    function transferAssetFromDepositPool(
        address _asset,
        uint256 _amount
    )
        external
        onlySupportedERC20Token(_asset)
        onlyAssetTransferRole
    {
        address lrtDepositPoolAddress = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);
        address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE);
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);

        ethValueInWithdrawal += (_amount * lrtOracle.getAssetPrice(_asset)) / 1e18;

        IERC20(_asset).safeTransferFrom(lrtDepositPoolAddress, address(this), _amount);
    }

    /// @notice send asset from LRTConverter to deposit pool
    /// @dev Only callable by Asset Transfer Role and asset needs to be approved
    /// @param _asset Asset address to send
    /// @param _amount Asset amount to send
    function transferAssetToDepositPool(
        address _asset,
        uint256 _amount
    )
        external
        onlySupportedERC20Token(_asset)
        onlyAssetTransferRole
    {
        address lrtDepositPoolAddress = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);
        address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE);
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);
        uint256 assetValue = (_amount * lrtOracle.getAssetPrice(_asset)) / 1e18;

        // Set to 0 if assetValue exceeds ethValueInWithdrawal, otherwise subtract assetValue
        ethValueInWithdrawal = ethValueInWithdrawal > assetValue ? ethValueInWithdrawal - assetValue : 0;

        IERC20(_asset).safeTransfer(lrtDepositPoolAddress, _amount);
    }

    /// @notice raises a unstake request for steth on lido
    /// @param amountToUnstake Amount of stETH to unstake
    function unstakeStEth(uint256 amountToUnstake)
        external
        nonReentrant
        onlyLRTOperator
        withinUnstakeLimits(amountToUnstake)
    {
        _unstakeStEth(amountToUnstake);
    }

    /// @notice claim eth from lido for steth and sends to deposit pool
    function claimStEth(uint256 _requestId, uint256 _hint) external nonReentrant onlyLRTOperator {
        _claimStEth(_requestId, _hint);
        _sendEthToDepositPool(address(this).balance);
    }

    /// @notice raises a unstake request for sweth on swell
    function unstakeSwEth(uint256 amountToUnstake) external nonReentrant onlyLRTOperator {
        _unstakeSwEth(amountToUnstake);
    }

    /// @notice claim eth from sweth from swell for sweth and sends to deposit pool
    function claimSwEth(uint256 _tokenId) external nonReentrant onlyLRTOperator {
        _claimSwEth(_tokenId);
        _sendEthToDepositPool(address(this).balance);
    }

    /// @notice Add or remove a user from the whitelist
    /// @param user User address
    /// @param whitelisted Whether to whitelist or remove from whitelist
    function setUserWhitelisted(address user, bool whitelisted) external onlyLRTManager {
        whitelistedUsers[user] = whitelisted;
        emit UserWhitelisted(user, whitelisted);
    }

    /// @notice Batch add or remove users from the whitelist
    /// @param users Array of user addresses
    /// @param whitelisted Whether to whitelist or remove from whitelist
    function batchSetUserWhitelisted(address[] calldata users, bool whitelisted) external onlyLRTManager {
        for (uint256 i = 0; i < users.length; i++) {
            whitelistedUsers[users[i]] = whitelisted;
            emit UserWhitelisted(users[i], whitelisted);
        }
    }

    /// @notice Declare withdrawal intent (only whitelisted users)
    /// @param amount Amount of stETH to declare for withdrawal
    function declareWithdrawalIntent(uint256 amount) external nonReentrant onlyWhitelistedUser {
        if (amount == 0) {
            revert InvalidAmount();
        }
        uint256 maxWhitelistedAllowance = 1_000_000_000 ether;
        if (whitelistedUnstakeAllowance + amount > maxWhitelistedAllowance) {
            revert WhitelistedAllowanceExceeded();
        }

        whitelistedUnstakeAllowance = whitelistedUnstakeAllowance + amount;
        emit WithdrawalIntentDeclared(msg.sender, amount);
    }

    /*////////////////////////////////////////////////////////////
                        view functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the current unstaking limits and usage
    /// @return whitelistedAllowance Current whitelisted unstake allowance
    /// @return activeETHWithdrawals Current active ETH withdrawals
    function getUnstakeLimits() external view returns (uint256 whitelistedAllowance, uint256 activeETHWithdrawals) {
        whitelistedAllowance = whitelistedUnstakeAllowance;
        activeETHWithdrawals = _getActiveETHUserWithdrawals();
    }

    /// @notice Check if a user is whitelisted
    /// @param user User address to check
    /// @return True if user is whitelisted
    function isUserWhitelisted(address user) public view returns (bool) {
        return whitelistedUsers[user];
    }

    /*////////////////////////////////////////////////////////////
                        internal functions
    //////////////////////////////////////////////////////////////*/

    function _sendEthToDepositPool(uint256 _amount) internal {
        address lrtDepositPoolAddress = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        if (ethValueInWithdrawal > _amount) {
            ethValueInWithdrawal -= _amount;
        } else {
            ethValueInWithdrawal = 0;
        }
        // Send eth to deposit pool
        ILRTDepositPool(lrtDepositPoolAddress).receiveFromLRTConverter{ value: _amount }();
        emit EthTransferred(lrtDepositPoolAddress, _amount);
    }

    /// @dev Get active user ETH withdrawals from LRTWithdrawalManager
    function _getActiveETHUserWithdrawals() internal view returns (uint256 activeETHWithdrawals) {
        ILRTWithdrawalManager lrtWithdrawalManager =
            ILRTWithdrawalManager(lrtConfig.getContract(LRTConstants.LRT_WITHDRAW_MANAGER));
        activeETHWithdrawals = lrtWithdrawalManager.assetsCommitted(LRTConstants.ETH_TOKEN);
    }
}
ILRTConfig.sol 57 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

interface ILRTConfig {
    // Errors
    error ValueAlreadyInUse();
    error AssetAlreadySupported();
    error AssetNotSupported();
    error ETHNotSupported();
    error CallerNotLRTConfigAdmin();
    error CallerNotLRTConfigManager();
    error CallerNotLRTConfigOperator();
    error CallerNotLRTConfigAssetTransferRole();
    error CallerNotLRTConfigOperatorOrAssetTransferRole();
    error CallerNotLRTConfigAllowedRole(string role);
    error CannotUpdateStrategyAsItHasFundsNDCFunds(address ndc, uint256 amount);
    error InvalidMaxRewardAmount();
    error ProtocolFeeExceedsLimit();
    error CannotRemoveAssetWithDeposits(address asset);
    error TokenNotFoundError();
    error InvalidDepositLimit();

    // Events

    event SetToken(bytes32 key, address indexed tokenAddr);
    event SetContract(bytes32 key, address indexed contractAddr);
    event AddedNewSupportedAsset(address indexed asset, uint256 depositLimit);
    event RemovedSupportedAsset(address indexed asset);
    event AssetDepositLimitUpdate(address indexed asset, uint256 depositLimit);
    event AssetStrategyUpdate(address indexed asset, address indexed strategy);
    event SetRSETH(address indexed rsETH);
    event UpdateMaxRewardAmount(uint256 maxRewardAmount);
    event MaxNegligibleAmountUpdated(uint256 maxNegligibleAmount);
    event UpdateFee(uint256 newFee);
    event SetEigenLayerRewardReceiver(address indexed eigenLayerRewardReceiver);
    event PausedAll(address indexed sender);

    // methods

    function rsETH() external view returns (address);

    function assetStrategy(address asset) external view returns (address);

    function isSupportedAsset(address asset) external view returns (bool);

    function getLSTToken(bytes32 tokenId) external view returns (address);

    function getContract(bytes32 contractId) external view returns (address);

    function getSupportedAssetList() external view returns (address[] memory);

    function depositLimitByAsset(address asset) external view returns (uint256);

    function protocolFeeInBPS() external view returns (uint256);

    function eigenLayerRewardReceiver() external view returns (address);
}
ILRTConverter.sol 31 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

interface ILRTConverter {
    error UserNotWhitelisted();
    error InvalidAmount();
    error UnstakeLimitExceeded();
    error WhitelistedAllowanceExceeded();
    error NotEnoughAssetToTransfer();

    event ConvertedEigenlayerAssetToRsEth(address indexed receiver, uint256 rsethAmount, bytes32 withdrawalRoot);
    event ETHSwappedForLST(uint256 ethAmount, address indexed toAsset, uint256 returnAmount);
    event EthTransferred(address to, uint256 amount);

    // stETH unstaking limit events
    event UserWhitelisted(address indexed user, bool whitelisted);
    event WithdrawalIntentDeclared(address indexed user, uint256 amount);

    function ethValueInWithdrawal() external view returns (uint256);

    function transferAssetFromDepositPool(address _asset, uint256 _amount) external;

    function setUserWhitelisted(address user, bool whitelisted) external;
    function batchSetUserWhitelisted(address[] calldata users, bool whitelisted) external;
    function declareWithdrawalIntent(uint256 amount) external;

    // View functions
    function getUnstakeLimits() external view returns (uint256 whitelistedAllowance, uint256 activeETHWithdrawals);
    function isUserWhitelisted(address user) external view returns (bool);
    function whitelistedUnstakeAllowance() external view returns (uint256);
}
ILRTDepositPool.sol 102 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

interface ILRTDepositPool {
    //errors
    error InvalidAmountToDeposit();
    error NotEnoughAssetToTransfer();
    error MaximumDepositLimitReached();
    error MaximumNodeDelegatorLimitReached();
    error InvalidMaximumNodeDelegatorLimit();
    error MinimumAmountToReceiveNotMet();
    error NodeDelegatorNotFound();
    error NodeDelegatorHasAssetBalance(address assetAddress, uint256 assetBalance);
    error NodeDelegatorHasETH();
    error EthTransferFailed();
    error NotEnoughETHToTransfer();
    error ZeroAssetAmount();
    error MinAssetAmountNotMet();
    //events

    event MaxNodeDelegatorLimitUpdated(uint256 maxNodeDelegatorLimit);
    event NodeDelegatorAddedinQueue(address nodeDelegatorContracts);
    event NodeDelegatorRemovedFromQueue(address nodeDelegatorContracts);
    event AssetDeposit(
        address indexed depositor,
        address indexed asset,
        uint256 depositAmount,
        uint256 rsethMintAmount,
        string referralId
    );
    event ETHDeposit(address indexed depositor, uint256 depositAmount, uint256 rsethMintAmount, string referralId);
    event MinAmountToDepositUpdated(uint256 minAmountToDeposit);
    event MaxNegligibleAmountUpdated(uint256 maxNegligibleAmount);
    event ETHSwappedForLST(uint256 ethAmount, address indexed toAsset, uint256 returnAmount);
    event AssetSwappedForETH(address indexed fromAsset, uint256 fromAssetAmount, uint256 returnAmount);
    event EthTransferred(address to, uint256 amount);
    event AssetStaked(address indexed asset, uint256 ethAmount, uint256 stETHShares);

    // functions
    function depositETH(uint256 minRSETHAmountExpected, string calldata referralId) external payable;

    function depositAsset(
        address asset,
        uint256 depositAmount,
        uint256 minRSETHAmountExpected,
        string calldata referralId
    )
        external;

    function getSwapETHToAssetReturnAmount(
        address toAsset,
        uint256 ethAmountToSend
    )
        external
        view
        returns (uint256 returnAmount);

    function getTotalAssetDeposits(address asset) external view returns (uint256);

    function getAssetCurrentLimit(address asset) external view returns (uint256);

    function getRsETHAmountToMint(address asset, uint256 depositAmount) external view returns (uint256);

    function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContract) external;

    function transferAssetToNodeDelegator(uint256 ndcIndex, address asset, uint256 amount) external;

    function updateMaxNodeDelegatorLimit(uint256 maxNodeDelegatorLimit) external;

    function getNodeDelegatorQueue() external view returns (address[] memory);

    function getAssetDistributionData(address asset)
        external
        view
        returns (
            uint256 assetLyingInDepositPool,
            uint256 assetLyingInNDCs,
            uint256 assetStakedInEigenLayer,
            uint256 assetUnstakingFromEigenLayer,
            uint256 assetLyingInConverter,
            uint256 assetLyingUnstakingVault
        );

    function getETHDistributionData()
        external
        view
        returns (
            uint256 ethLyingInDepositPool,
            uint256 ethLyingInNDCs,
            uint256 ethStakedInEigenLayer,
            uint256 ethUnstakingFromEigenLayer,
            uint256 ethLyingInConverter,
            uint256 ethLyingInUnstakingVault
        );

    function isNodeDelegator(address nodeDelegatorContract) external view returns (uint256);

    // receivers
    function receiveFromRewardReceiver() external payable;
    function receiveFromLRTConverter() external payable;
    function receiveFromNodeDelegator() external payable;
}
ILRTOracle.sol 32 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

interface ILRTOracle {
    // errors
    error AssetOracleNotSupported();
    error InvalidPriceOracle();
    error PriceAboveDailyThreshold();
    error ProtocolFeeExceededTVLIncrease();
    error DailyFeeMintLimitExceeded(uint256 currentAmount, uint256 maxAmount);
    error ContractPaused();
    error ContractNotPaused();
    error PeriodStartTimeShouldBeWithin24Hours();

    // events
    event AssetPriceOracleUpdate(address indexed asset, address indexed priceOracle);
    event RsETHPriceUpdate(uint256 newPrice, uint256 oldPrice);
    event PricePercentageLimitUpdate(uint256 newLimit);
    event FeeMinted(address treasury, uint256 rsethAmount);
    event RsETHPriceBelowPeak(uint256 highestRsethPrice, uint256 newPrice);
    event RsETHPriceDecrease(uint256 newPrice, uint256 oldPrice);
    event PeriodInTimestampUpdate(uint256 newPeriod);
    event MaxFeeMintAmountPerDayUpdated(uint256 newLimit);
    event Paused(address account);
    event Unpaused(address account);
    event FeePeriodStartTimeSet(uint256 feePeriodStartTime);

    // methods
    function getAssetPrice(address asset) external view returns (uint256);
    function assetPriceOracle(address asset) external view returns (address);
    function rsETHPrice() external view returns (uint256);
}
ILRTWithdrawalManager.sol 149 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

interface ILRTWithdrawalManager {
    //errors
    error EthTransferFailed();
    error InvalidAmountToWithdraw();
    error ExceedAmountToWithdraw();
    error WithdrawalLocked();
    error WithdrawalDelayNotPassed();
    error ExceedWithdrawalDelay();
    error NoPendingWithdrawals();
    error AmountMustBeGreaterThanZero();
    error StrategyNotSupported();

    error RsETHPriceOutOfPriceRange(uint256 rsEthPrice);
    error AssetPriceOutOfPriceRange(uint256 assetPrice);
    error CantInstantWithdrawMoreThanAvailable();
    error NotEnoughRsETH();
    error InstantWithdrawalNotEnabled();
    error FeeTooHigh();
    error PendingWithdrawalsExist();
    error TreasuryTransferFailed();
    error NoWithdrawalRequests(address user, address asset);
    error AaveIntegrationNotEnabled();
    error InsufficientAaveBalance();
    error InvalidAaveConfiguration();
    error AaveHealthCheckFailed();
    error InsufficientLiquidityForWithdrawal();
    error UnauthorizedCaller();
    error AaveIntegrationAlreadyInDesiredState(bool enabled);

    struct UnlockParams {
        uint256 rsETHPrice;
        uint256 assetPrice;
        uint256 totalAvailableAssets;
    }

    struct WithdrawalRequest {
        uint256 rsETHUnstaked;
        uint256 expectedAssetAmount;
        uint256 withdrawalStartBlock;
    }

    //events
    event AssetWithdrawalQueued(
        address indexed withdrawer, address indexed asset, uint256 rsETHUnstaked, uint256 indexed userNonce
    );

    event AssetWithdrawalFinalized(
        address indexed withdrawer, address indexed asset, uint256 amountBurned, uint256 amountReceived
    );
    event EtherReceived(address indexed depositor, uint256 ethAmount, uint256 sharesAmount);

    event AssetUnlocked(
        address indexed asset, uint256 rsEthAmount, uint256 assetAmount, uint256 rsEThPrice, uint256 assetPrice
    );

    event MinAmountToWithdrawUpdated(address asset, uint256 minRsEthAmountToWithdraw);
    event WithdrawalDelayBlocksUpdated(uint256 withdrawalDelayBlocks);

    event ReferralIdEmitted(string referralId);
    event InstantWithdrawalEnabledUpdated(address indexed asset, bool enabled);
    event InstantWithdrawalFeeUpdated(uint256 feeBasisPoints);
    event InstantWithdrawalFeeRecipientUpdated(address indexed feeRecipient);
    event InstantWithdrawalFeeCollected(address indexed user, address indexed asset, uint256 fee);
    event TreasuryWithdrawalSettled(address indexed asset, uint256 amount);
    event RemainingAssetsSwept(address indexed asset, uint256 amount, address indexed treasury);
    event AssetWithdrawalCompletedBy(address indexed user);
    event AaveIntegrationConfigured(
        address indexed aavePool, address indexed aaveWETHGateway, address indexed aaveAWETH, address aaveDataProvider
    );
    event AaveIntegrationEnabled(bool enabled);
    event ETHDepositedToAave(uint256 amount, uint256 totalDeposited);
    event ETHWithdrawnFromAave(uint256 amount, uint256 totalDeposited);
    event InterestCollectedToTreasury(uint256 interestAmount, address indexed treasury);
    event EmergencyWithdrawFromAave(uint256 amount, address indexed recipient);
    event AaveDepositFailed(uint256 amount, bytes reason);

    // methods
    function getExpectedAssetAmount(address asset, uint256 amount) external view returns (uint256);

    function getAvailableAssetAmount(address asset) external view returns (uint256 assetAmount);

    function getUserWithdrawalRequest(
        address asset,
        address user,
        uint256 index
    )
        external
        view
        returns (uint256 rsETHAmount, uint256 expectedAssetAmount, uint256 withdrawalStartBlock, uint256 userNonce);

    function initiateWithdrawal(address asset, uint256 withdrawAmount, string calldata referralId) external;

    function completeWithdrawal(address asset, string calldata referralId) external;

    function completeWithdrawalForUser(address asset, address user, string calldata referralId) external;

    function unlockQueue(
        address asset,
        uint256 index,
        uint256 minimumAssetPrice,
        uint256 minimumRsEthPrice,
        uint256 maximumAssetPrice,
        uint256 maximumRsEthPrice
    )
        external
        returns (uint256 rsETHBurned, uint256 assetAmountUnlocked);

    // receive functions
    function receiveFromLRTUnstakingVault() external payable;

    function setInstantWithdrawalEnabled(address asset, bool enabled) external;

    function setInstantWithdrawalFee(uint256 feeBasisPoints) external;

    function setInstantWithdrawalFeeRecipient(address feeRecipient) external;

    function assetsCommitted(address asset) external view returns (uint256);

    // Treasury withdrawal flow functions
    function hasUnlockedWithdrawals(address asset) external view returns (bool);

    function sweepRemainingAssets(address asset) external returns (uint256 transferredAmount);

    // Aave integration functions
    function configureAaveIntegration(
        address aavePool_,
        address aaveWETHGateway_,
        address aaveAWETH_,
        address aaveDataProvider_
    )
        external;

    function setAaveIntegrationEnabled(bool enabled) external;

    function depositIdleETHToAave(uint256 amount) external;

    function collectInterestToTreasury() external returns (uint256 interestAmount);

    function emergencyWithdrawFromAave(uint256 amount) external;

    function getAaveBalance() external view returns (uint256);

    function getAccruedInterest() external view returns (uint256);

    function aaveHealthCheck() external view returns (bool);
}
UnstakeStETH.sol 66 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface ILidoWithdrawalQueue {
    function requestWithdrawals(
        uint256[] calldata _amounts,
        address _owner
    )
        external
        returns (uint256[] memory requestIds);

    ///  Usage: findCheckpointHints(_requestIds, 1, getLastCheckpointIndex())
    function findCheckpointHints(
        uint256[] calldata _requestIds,
        uint256 _firstIndex,
        uint256 _lastIndex
    )
        external
        view
        returns (uint256[] memory hintIds);

    function getLastCheckpointIndex() external view returns (uint256);

    function claimWithdrawalsTo(uint256[] calldata _requestIds, uint256[] calldata _hints, address _recipient) external;
    function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable;

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

/// @dev Upgradeable base contract without storage gaps; adding variables in upgrades can collide with inheritors.
abstract contract UnstakeStETH is Initializable {
    using SafeERC20 for IERC20;

    ILidoWithdrawalQueue public withdrawalQueue;
    IERC20 public stETH;

    event UnstakeStETHStarted(uint256 tokenId);

    function __initializeStETH(address _withdrawalQueue, address _stETHAddress) internal onlyInitializing {
        withdrawalQueue = ILidoWithdrawalQueue(_withdrawalQueue);
        stETH = IERC20(_stETHAddress);
    }

    function _unstakeStEth(uint256 amountToUnstake) internal {
        stETH.safeIncreaseAllowance(address(withdrawalQueue), amountToUnstake);

        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amountToUnstake;

        uint256[] memory requestIds = withdrawalQueue.requestWithdrawals(amounts, address(this));

        emit UnstakeStETHStarted(requestIds[0]);
    }

    function _claimStEth(uint256 _requestId, uint256 _hint) internal {
        uint256[] memory requestIds = new uint256[](1);
        uint256[] memory hints = new uint256[](1);
        requestIds[0] = _requestId;
        hints[0] = _hint;
        withdrawalQueue.claimWithdrawalsTo(requestIds, hints, address(this));
    }
}
UnstakeSwETH.sol 42 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IswEXIT {
    function getLastTokenIdCreated() external view returns (uint256);

    function createWithdrawRequest(uint256 amount) external;
    function finalizeWithdrawal(uint256 tokenId) external;
    function processWithdrawals(uint256 lastTokenIdToProcess) external;
}

/// @dev Upgradeable base contract without storage gaps; adding variables in upgrades can collide with inheritors.
abstract contract UnstakeSwETH is Initializable {
    using SafeERC20 for IERC20;

    IswEXIT public swEXIT;
    IERC20 public swETH;

    event UnstakeSwETHStarted(uint256 tokenId);

    function __initializeSwETH(address _swEXITAddress, address _swETHAddress) internal onlyInitializing {
        swEXIT = IswEXIT(_swEXITAddress);
        swETH = IERC20(_swETHAddress);
    }

    function _unstakeSwEth(uint256 amountToUnstake) internal returns (uint256 tokenId) {
        swETH.safeIncreaseAllowance(address(swEXIT), amountToUnstake);

        // Create withdrawal request
        swEXIT.createWithdrawRequest(amountToUnstake);
        tokenId = swEXIT.getLastTokenIdCreated();
        emit UnstakeSwETHStarted(tokenId);
    }

    function _claimSwEth(uint256 _tokenId) internal {
        swEXIT.finalizeWithdrawal(_tokenId);
    }
}
LRTConfigRoleChecker.sol 81 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

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

import { ILRTConfig } from "../interfaces/ILRTConfig.sol";

import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";

/// @title LRTConfigRoleChecker - LRT Config Role Checker Contract
/// @notice Handles LRT config role checks
abstract contract LRTConfigRoleChecker {
    ILRTConfig public lrtConfig;

    // events
    event UpdatedLRTConfig(address indexed lrtConfig);

    // modifiers
    modifier onlyRole(bytes32 role) {
        if (!IAccessControl(address(lrtConfig)).hasRole(role, msg.sender)) {
            string memory roleStr = string(abi.encodePacked(role));
            revert ILRTConfig.CallerNotLRTConfigAllowedRole(roleStr);
        }
        _;
    }

    modifier onlyLRTManager() {
        if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.MANAGER, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigManager();
        }
        _;
    }

    modifier onlyLRTOperator() {
        if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.OPERATOR_ROLE, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigOperator();
        }
        _;
    }

    modifier onlyAssetTransferRole() {
        if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.ASSET_TRANSFER_ROLE, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAssetTransferRole();
        }
        _;
    }

    modifier onlyAssetTransferOrOperatorRole() {
        if (
            !IAccessControl(address(lrtConfig)).hasRole(LRTConstants.ASSET_TRANSFER_ROLE, msg.sender)
                && !IAccessControl(address(lrtConfig)).hasRole(LRTConstants.OPERATOR_ROLE, msg.sender)
        ) {
            revert ILRTConfig.CallerNotLRTConfigOperatorOrAssetTransferRole();
        }
        _;
    }

    modifier onlyLRTAdmin() {
        if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.DEFAULT_ADMIN_ROLE, msg.sender)) {
            revert ILRTConfig.CallerNotLRTConfigAdmin();
        }
        _;
    }

    modifier onlySupportedAsset(address asset) {
        if (!lrtConfig.isSupportedAsset(asset)) {
            revert ILRTConfig.AssetNotSupported();
        }
        _;
    }

    modifier onlySupportedERC20Token(address asset) {
        if (!lrtConfig.isSupportedAsset(asset)) {
            revert ILRTConfig.AssetNotSupported();
        }
        if (asset == LRTConstants.ETH_TOKEN) {
            revert ILRTConfig.ETHNotSupported();
        }
        _;
    }
}
LRTConstants.sol 96 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

import { ILRTConfig } from "../interfaces/ILRTConfig.sol";

library LRTConstants {
    //tokens
    bytes32 public constant ST_ETH_TOKEN = keccak256("ST_ETH_TOKEN");
    bytes32 public constant ETHX_TOKEN = keccak256("ETHX_TOKEN");
    bytes32 public constant SFRX_ETH_TOKEN = keccak256("SFRX_ETH_TOKEN");
    // native ETH as ERC20 for ease of implementation
    address public constant ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    //contracts
    bytes32 public constant LRT_ORACLE = keccak256("LRT_ORACLE");
    bytes32 public constant LRT_DEPOSIT_POOL = keccak256("LRT_DEPOSIT_POOL");
    bytes32 public constant LRT_WITHDRAW_MANAGER = keccak256("LRT_WITHDRAW_MANAGER");
    bytes32 public constant LRT_UNSTAKING_VAULT = keccak256("LRT_UNSTAKING_VAULT");
    bytes32 public constant LRT_CONVERTER = keccak256("LRT_CONVERTER");
    bytes32 public constant REWARD_RECEIVER = keccak256("REWARD_RECEIVER");
    bytes32 public constant PROTOCOL_TREASURY = keccak256("PROTOCOL_TREASURY");
    bytes32 public constant PUBKEY_REGISTRY = keccak256("PUBKEY_REGISTRY");
    bytes32 public constant UNLOCKED_WITHDRAWAL_INITIALIZER = keccak256("UNLOCKED_WITHDRAWAL_INITIALIZER");

    bytes32 public constant BEACON_CHAIN_ETH_STRATEGY = keccak256("BEACON_CHAIN_ETH_STRATEGY");
    bytes32 public constant EIGEN_STRATEGY_MANAGER = keccak256("EIGEN_STRATEGY_MANAGER");
    bytes32 public constant EIGEN_POD_MANAGER = keccak256("EIGEN_POD_MANAGER");
    bytes32 public constant EIGEN_DELEGATION_MANAGER = keccak256("EIGEN_DELEGATION_MANAGER");
    bytes32 public constant EIGEN_REWARDS_COORDINATOR = keccak256("EIGEN_REWARDS_COORDINATOR");

    //Roles
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    bytes32 public constant MANAGER = keccak256("MANAGER");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    bytes32 public constant ASSET_TRANSFER_ROLE = keccak256("ASSET_TRANSFER_ROLE");
    bytes32 public constant TIME_LOCK_ROLE = keccak256("TIME_LOCK_ROLE");

    // constants
    uint256 public constant ONE_E_9 = 1e9;

    // Contract getters
    function unstakingVault(ILRTConfig config) internal view returns (address) {
        return config.getContract(LRT_UNSTAKING_VAULT);
    }

    function delegationManager(ILRTConfig config) internal view returns (address) {
        return config.getContract(EIGEN_DELEGATION_MANAGER);
    }

    function rewardsCoordinator(ILRTConfig config) internal view returns (address) {
        return config.getContract(EIGEN_REWARDS_COORDINATOR);
    }

    function strategyManager(ILRTConfig config) internal view returns (address) {
        return config.getContract(EIGEN_STRATEGY_MANAGER);
    }

    function lrtConverter(ILRTConfig config) internal view returns (address) {
        return config.getContract(LRT_CONVERTER);
    }

    function depositPool(ILRTConfig config) internal view returns (address) {
        return config.getContract(LRT_DEPOSIT_POOL);
    }

    function lrtOracle(ILRTConfig config) internal view returns (address) {
        return config.getContract(LRT_ORACLE);
    }

    function rewardReceiver(ILRTConfig config) internal view returns (address) {
        return config.getContract(REWARD_RECEIVER);
    }

    function protocolTreasury(ILRTConfig config) internal view returns (address) {
        return config.getContract(PROTOCOL_TREASURY);
    }

    function pubkeyRegistry(ILRTConfig config) internal view returns (address) {
        return config.getContract(PUBKEY_REGISTRY);
    }

    function beaconChainETHStrategy(ILRTConfig config) internal view returns (address) {
        return config.getContract(BEACON_CHAIN_ETH_STRATEGY);
    }

    function eigenPodManager(ILRTConfig config) internal view returns (address) {
        return config.getContract(EIGEN_POD_MANAGER);
    }

    function withdrawManager(ILRTConfig config) internal view returns (address) {
        return config.getContract(LRT_WITHDRAW_MANAGER);
    }
}
UtilLib.sol 24 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;

/// @title UtilLib - Utility library
/// @notice Utility functions
library UtilLib {
    error ZeroAddressNotAllowed();

    /// @dev zero address check modifier
    /// @param address_ address to check
    function checkNonZeroAddress(address address_) internal pure {
        if (address_ == address(0)) revert ZeroAddressNotAllowed();
    }

    function getMin(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a < b) return a;
        return b;
    }

    function getMax(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a > b) return a;
        return b;
    }
}

Read Contract

_legacyConversionLimit 0x458bfa01 → uint256
_legacyConvertibleAssets 0x7a8dce35 → bool
_legacyProcessedWithdrawalRoots 0xcc169822 → bool
ethValueInWithdrawal 0x6a4c410d → uint256
getUnstakeLimits 0x3ea29f6c → uint256, uint256
isUserWhitelisted 0xfb237eb2 → bool
lrtConfig 0xf1650a46 → address
onERC721Received 0x150b7a02 → bytes4
stETH 0xc1fe3e48 → address
swETH 0x18a66953 → address
swEXIT 0xe1bce57a → address
whitelistedUnstakeAllowance 0x49bdaa15 → uint256
withdrawalQueue 0x37d5fe99 → address

Write Contract 11 functions

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

batchSetUserWhitelisted 0x15b81979
address[] users
bool whitelisted
claimStEth 0x2f0d3d83
uint256 _requestId
uint256 _hint
claimSwEth 0x0dd4433d
uint256 _tokenId
declareWithdrawalIntent 0xe00a130d
uint256 amount
initialize 0xc4d66de8
address lrtConfigAddr
initialize2 0xf3cbc0f5
address _withdrawalQueueAddress
address _stETHAddress
address _swEXITAddress
address _swETHAddress
setUserWhitelisted 0x81889321
address user
bool whitelisted
transferAssetFromDepositPool 0xa881cda2
address _asset
uint256 _amount
transferAssetToDepositPool 0x056b56cd
address _asset
uint256 _amount
unstakeStEth 0x23f87d01
uint256 amountToUnstake
unstakeSwEth 0x5e7a000f
uint256 amountToUnstake

Recent Transactions

This address has 1 on-chain transactions, but only 0.8% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →