Address Contract Verified
Address
0x70dAf8B0BFc846cc98b71D2F8FfdC91f4D2bbd51
Balance
0 ETH
Nonce
1
Code Size
11070 bytes
Creator
0x5DB1955f...5F27 at tx 0xc88863e2...e21bd8
Indexed Transactions
0 (1 on-chain, 0.8% indexed)
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 →