diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 620f0892..e38e476d 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -19,6 +19,24 @@ The project is currently under development and may change in the future. - [Call](#call) - [Populate transaction](#populate-transaction) - [Simulate transaction](#simulate-transaction) +- [Withdrawal](#withdrawal) + + - [Call](#call-1) + + - [Send request withdrawal with Permit](#send-request-withdrawal-with-permit) + - [Send request withdrawal with preallocated allowance](#send-request-withdrawal-with-preallocated-allowance) + - [`Request` other methods](#request-other-methods) + + - [Utils](#utils) + + - [Set allowance of WithdrawalQueue contract](#set-allowance-of-withdrawalqueue-contract) + - [`Allowance` other methods](#allowance-other-methods) + + - [Views](#views) + + - [Constants](#constants) + - [Requests info](#requests-info) + - [Lido contract addresses](#lido-contract-addresses) ## Installation @@ -157,6 +175,7 @@ Callback stages: import { LidoSDK, StakeStageCallback, + StakeCallbackStages, SDKError, } from '@lidofinance/lido-ethereum-sdk'; @@ -170,22 +189,22 @@ lidoSDK.core.defineWeb3Provider(); const callback: StakeStageCallback = ({ stage, payload }) => { switch (stage) { - case StakeCallbackStage.SIGN: + case StakeCallbackStages.SIGN: console.log('wait for sign'); break; - case StakeCallbackStage.RECEIPT: + case StakeCallbackStages.RECEIPT: console.log('wait for receipt'); console.log(payload, 'transaction hash'); break; - case StakeCallbackStage.CONFIRMATION: + case StakeCallbackStages.CONFIRMATION: console.log('wait for confirmation'); console.log(payload, 'transaction receipt'); break; - case StakeCallbackStage.DONE: + case StakeCallbackStages.DONE: console.log('done'); console.log(payload, 'transaction confirmations'); break; - case StakeCallbackStage.ERROR: + case StakeCallbackStages.ERROR: console.log('error'); console.log(payload, 'error object with code and message'); break; @@ -248,6 +267,354 @@ const simulateResult = await lidoSDK.staking.stakeSimulateTx({ }); ``` +## Withdrawal + +### Call + +#### Send request withdrawal with Permit + +`Supports EOA and Multisig` + +```ts +import { + LidoSDK, + RequestStageCallback, + RequestCallbackStages, + SDKError, +} from '@lidofinance/lido-ethereum-sdk'; + +const lidoSDK = new LidoSDK({ + rpcUrls: ['https://rpc-url'], + chainId: 5, +}); + +// Define default web3 provider in sdk (window.ethereum) if web3Provider is not defined in constructor +lidoSDK.core.defineWeb3Provider(); + +const callback: RequestStageCallback = ({ stage, payload }) => { + switch (stage) { + case RequestCallbackStages.PERMIT: + console.log('wait for permit'); + break; + case RequestCallbackStages.GAS_LIMIT: + console.log('wait for gas limit'); + break; + case RequestCallbackStages.SIGN: + console.log('wait for sign'); + break; + case RequestCallbackStages.RECEIPT: + console.log('wait for receipt'); + console.log(payload, 'transaction hash'); + break; + case RequestCallbackStages.CONFIRMATION: + console.log('wait for confirmation'); + console.log(payload, 'transaction receipt'); + break; + case RequestCallbackStages.DONE: + console.log('done'); + console.log(payload, 'transaction confirmations'); + break; + case RequestCallbackStages.MULTISIG_DONE: + console.log('multisig_done'); + console.log(payload, 'transaction confirmations'); + break; + case RequestCallbackStages.ERROR: + console.log('error'); + console.log(payload, 'error object with code and message'); + break; + default: + } +}; + +try { + const requestResult = await lidoSDK.withdrawals.request({ + amount, + requests, + token, // 'stETH' | 'wstETH' + callback, + account, + }); + + console.log( + requestResult, + 'transaction hash, transaction receipt, confirmations', + ); +} catch (error) { + console.log((error as SDKError).errorMessage, (error as SDKError).code); +} +``` + +#### Send request withdrawal with preallocated allowance + +`Supports EOA and Multisig` + +```ts +import { + LidoSDK, + RequestStageCallback, + RequestCallbackStages, + SDKError, +} from '@lidofinance/lido-ethereum-sdk'; + +const lidoSDK = new LidoSDK({ + rpcUrls: ['https://rpc-url'], + chainId: 5, +}); + +// Define default web3 provider in sdk (window.ethereum) if web3Provider is not defined in constructor +lidoSDK.core.defineWeb3Provider(); + +const callback: RequestStageCallback = ({ stage, payload }) => { + switch (stage) { + case RequestCallbackStages.GAS_LIMIT: + console.log('wait for gas limit'); + break; + case RequestCallbackStages.SIGN: + console.log('wait for sign'); + break; + case RequestCallbackStages.RECEIPT: + console.log('wait for receipt'); + console.log(payload, 'transaction hash'); + break; + case RequestCallbackStages.CONFIRMATION: + console.log('wait for confirmation'); + console.log(payload, 'transaction receipt'); + break; + case RequestCallbackStages.DONE: + console.log('done'); + console.log(payload, 'transaction confirmations'); + break; + case RequestCallbackStages.ERROR: + console.log('error'); + console.log(payload, 'error object with code and message'); + break; + default: + } +}; + +try { + const requestResult = await lidoSDK.withdrawals.requestWithoutPermit({ + requests, + token, // 'stETH' | 'wstETH' + callback, + account, + }); + + console.log( + requestResult, + 'transaction hash, transaction receipt, confirmations', + ); +} catch (error) { + console.log((error as SDKError).errorMessage, (error as SDKError).code); +} +``` + +#### `Request` other methods + +##### EOA + +- requestStethWithPermit +- requestWstethWithPermit +- requestStethWithoutPermit +- requestWstethWithoutPermit +- requestWithoutPermitByToken +- requestWithPermitByToken + +##### Multisig + +- requestStethMultisig +- requestWstethMultisig +- requestMultisigByToken + +### Utils + +#### Set allowance of WithdrawalQueue contract + +`Supports EOA and Multisig` + +```ts +import { + LidoSDK, + ApproveCallbackStages, + ApproveStageCallback, + SDKError, +} from '@lidofinance/lido-ethereum-sdk'; + +const lidoSDK = new LidoSDK({ + rpcUrls: ['https://rpc-url'], + chainId: 5, +}); + +// Define default web3 provider in sdk (window.ethereum) if web3Provider is not defined in constructor +lidoSDK.core.defineWeb3Provider(); + +const callback: ApproveStageCallback = ({ stage, payload }) => { + switch (stage) { + case ApproveCallbackStages.GAS_LIMIT: + console.log('wait for gas limit'); + break; + case ApproveCallbackStages.SIGN: + console.log('wait for sign'); + break; + case ApproveCallbackStages.RECEIPT: + console.log('wait for receipt'); + console.log(payload, 'transaction hash'); + break; + case ApproveCallbackStages.CONFIRMATION: + console.log('wait for confirmation'); + console.log(payload, 'transaction receipt'); + break; + case ApproveCallbackStages.DONE: + console.log('done'); + console.log(payload, 'transaction confirmations'); + break; + case ApproveCallbackStages.ERROR: + console.log('error'); + console.log(payload, 'error object with code and message'); + break; + default: + } +}; + +try { + const approveResult = await lidoSDK.withdrawals.approval.approve({ + amount, + token, // 'stETH' | 'wstETH' + callback, + account, + }); + + console.log( + approveResult, + 'transaction hash, transaction receipt, confirmations', + ); +} catch (error) { + console.log((error as SDKError).errorMessage, (error as SDKError).code); +} +``` + +#### `Allowance` other methods + +##### EOA + +- approveEOA +- approveSteth +- approveWsteth +- approveByToken + +##### Multisig + +- approveStethMultisig +- approveWstethMultisig +- approveMultisigByToken + +### Views + +- getWithdrawalRequestsIds +- getLastCheckpointIndex +- getWithdrawalStatus +- findCheckpointHints +- getClaimableEther +- getUnfinalizedStETH + +#### Constants + +- minStethWithdrawalAmount +- maxStethWithdrawalAmount +- isPaused +- isBunkerModeActive +- isTurboModeActive + +#### Requests info + +###### `getWithdrawalRequestsInfo` + +###### Input Parameters: + +- `props: { account: Address }` + - `account` (Type: Address): The account address. + +##### Output Parameters: + +- Type: Object +- Structure: + + - `claimableInfo` (Type: Object): Information about withdrawal requests that can be claimed. + - `claimableRequests` (Type: Array[RequestStatusWithId]): A list of requests with their statuses and identifiers. + - `claimableAmountStETH` (Type: bigint): The amount of ETH available for claiming. + - `pendingInfo` (Type: Object): Information about pending withdrawal requests. + - `pendingRequests` (Type: Array[RequestStatusWithId]): A list of requests with their statuses and identifiers. + - `pendingAmountStETH` (Type: bigint): The amount of ETH pending for withdrawal. + - `claimableETH` (Type: bigint): The amount of ETH available for claiming. + +###### `getWithdrawalRequestsStatus` + +###### Input Parameters: + +- `props: { account: Address }` + - `account` (Type: Address): The account address. + +##### Output Parameters: + +- Type: Array of RequestStatusWithId objects +- Structure of each object: + + - `id` (Type: bigint): The request identifier. + - `isFinalized` (Type: boolean): A flag indicating whether the request has been finalized. + - `isClaimed` (Type: boolean): A flag indicating whether the request has already been claimed. + - `amountOfStETH` (Type: bigint): The amount of stETH to be withdrawn. + - `amountOfShares` (Type: bigint): The amount of shares to be burned. + - `owner` (Type: Address): The account address that initiated the request. + - `timestamp` (Type: bigint): The timestamp of the request. + - `stringId` (Type: string): The request identifier in string format. + +###### `getClaimableRequestsInfo` + +###### Input Parameters: + +- `props: { account: Address }` + - `account` (Type: Address): The account address. + +###### Output Parameters: + +- Type: Object +- Structure: + + - `claimableRequests` (Type: Array[RequestStatusWithId]): A list of requests that can be claimed. + - `claimableAmountStETH` (Type: bigint): The amount of ETH available for claiming. + +###### `getClaimableRequestsETH` + +###### Input Parameters: + +- `props: { claimableRequestsIds: (bigint | RequestStatusWithId)[] }` + - `claimableRequestsIds` (Type: Array): An array of request identifiers for which information is needed. + +###### Output Parameters: + +- Type: Object +- Structure: + + - `ethByRequests` (Type: Array of bigint): A list of ETH amounts for each request. + - `ethSum` (Type: bigint): The sum of all ETH amounts in the list. + +- getPendingRequestsInfo + +###### `getPendingRequestsInfo` + +##### Input Parameters: + +- `props: { account: Address }` + - `account` (Type: Address): The account address. + +##### Output Parameters: + +- Type: Object +- Structure: + + - `pendingRequests` (Type: Array[RequestStatusWithId]): A list of requests pending finalization. + - `pendingAmountStETH` (Type: bigint): The amount of ETH pending claiming. + ## Lido contract addresses ```ts @@ -258,6 +625,10 @@ const lidoSDK = new LidoSDK({ chainId: 5, }); -const stethAddress = await core.getContractAddress(LIDO_CONTRACT_NAMES.lido); -const wsteth = await core.getContractAddress(LIDO_CONTRACT_NAMES.wsteth); +const stethAddress = await lidoSDK.core.getContractAddress( + LIDO_CONTRACT_NAMES.lido, +); +const wsteth = await lidoSDK.core.getContractAddress( + LIDO_CONTRACT_NAMES.wsteth, +); ``` diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c5be7cd2..b859ae4b 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -79,6 +79,7 @@ "clean": "rimraf dist" }, "dependencies": { + "@ethersproject/bytes": "^5.7.0", "tiny-invariant": "^1.3.1", "viem": "^1.6.7" }, diff --git a/packages/sdk/src/common/decorators/ErrorHandler.ts b/packages/sdk/src/common/decorators/ErrorHandler.ts new file mode 100644 index 00000000..3f137d20 --- /dev/null +++ b/packages/sdk/src/common/decorators/ErrorHandler.ts @@ -0,0 +1,91 @@ +import { type LidoSDKCore } from '../../core/index.js'; +import { callConsoleMessage } from './utils.js'; +import { HeadMessage } from './types.js'; + +const isBus = function ( + value: unknown, +): value is { bus: { core?: LidoSDKCore } } { + return !!value && typeof value === 'object' && 'bus' in value; +}; + +const isCore = function (value: unknown): value is { core?: LidoSDKCore } { + return !!value && typeof value === 'object' && 'core' in value; +}; + +const extractError = function (this: This, error: unknown) { + let txError = error; + + if (isBus(this)) { + const { message, code } = (this.bus.core as LidoSDKCore)?.getErrorMessage?.( + error, + ); + + txError = (this.bus?.core as LidoSDKCore)?.error?.({ + message, + error, + code, + }); + } else if (isCore(this)) { + const { message, code } = (this.core as LidoSDKCore)?.getErrorMessage?.( + error, + ); + txError = (this.core as LidoSDKCore)?.error?.({ + message, + error, + code, + }); + } + + return txError; +}; + +export const ErrorHandler = function (headMessage: HeadMessage = 'Error:') { + return function ErrorHandlerMethod( + originalMethod: (this: This, ...args: Args) => Return, + context: ClassMethodDecoratorContext< + This, + (this: This, ...args: Args) => Return + >, + ) { + const methodName = String(context.name); + const replacementMethod = function (this: This, ...args: Args): Return { + const callback = args[0].callback; + + try { + const result = originalMethod.call(this, ...args); + + if (result instanceof Promise) { + return result + .then((resolvedResult) => resolvedResult) + .catch((error) => { + callConsoleMessage( + headMessage, + `Error in method '${methodName}'.`, + 'Error:', + ); + + let txError = extractError.call(this, error); + callback?.({ stage: 'error', payload: txError }); + + throw txError; + }) as Return; + } else { + callConsoleMessage(headMessage, `Exiting method '${methodName}'.`); + return result; + } + } catch (error) { + callConsoleMessage( + headMessage, + `Error in method '${methodName}'.`, + 'Error:', + ); + + let txError = extractError.call(this, error); + callback?.({ stage: 'error', payload: txError }); + + throw txError; + } + }; + return replacementMethod; + }; +}; diff --git a/packages/sdk/src/common/decorators/constants.ts b/packages/sdk/src/common/decorators/constants.ts index 34360300..f3e41d28 100644 --- a/packages/sdk/src/common/decorators/constants.ts +++ b/packages/sdk/src/common/decorators/constants.ts @@ -1,15 +1,16 @@ -import { HeadMessage } from "./types.js"; +import { HeadMessage } from './types.js'; export const ConsoleCss: Record = { - "Provider:": "color: blue", - "Contracts:": "color: green", - "Balances:": "color: yellow", - "Utils:": "color: cyan", - "Views:": "color: aquamarine", - "Call:": "color: orange", - "Error:": "color: red", - "LOG:": "color: lightblue", - "Cache:": "color: mediumvioletred", - "Init:": - "color: #33F3FF;text-shadow: 0px 0px 0 #899CD5, 1px 1px 0 #8194CD, 2px 2px 0 #788BC4, 3px 3px 0 #6F82BB, 4px 4px 0 #677AB3, 5px 5px 0 #5E71AA, 6px 6px 0 #5568A1, 7px 7px 0 #4C5F98, 8px 8px 0 #445790, 9px 9px 0 #3B4E87, 10px 10px 0 #32457E, 11px 11px 0 #2A3D76, 12px 12px 0 #21346D, 13px 13px 0 #182B64, 14px 14px 0 #0F225B, 15px 15px 0 #071A53, 16px 16px 0 #02114A, 17px 17px 0 #0B0841, 18px 18px 0 #130039, 19px 19px 0 #1C0930, 20px 20px 0 #251227, 21px 21px 20px rgba(0,0,0,1), 21px 21px 1px rgba(0,0,0,0.5), 0px 0px 20px rgba(0,0,0,.2);font-size: 50px;", + 'Provider:': 'color: blue', + 'Contracts:': 'color: green', + 'Balances:': 'color: yellow', + 'Utils:': 'color: cyan', + 'Views:': 'color: aquamarine', + 'Call:': 'color: orange', + 'Error:': 'color: red', + 'LOG:': 'color: lightblue', + 'Cache:': 'color: mediumvioletred', + 'Permit:': 'color: lime', + 'Init:': + 'color: #33F3FF;text-shadow: 0px 0px 0 #899CD5, 1px 1px 0 #8194CD, 2px 2px 0 #788BC4, 3px 3px 0 #6F82BB, 4px 4px 0 #677AB3, 5px 5px 0 #5E71AA, 6px 6px 0 #5568A1, 7px 7px 0 #4C5F98, 8px 8px 0 #445790, 9px 9px 0 #3B4E87, 10px 10px 0 #32457E, 11px 11px 0 #2A3D76, 12px 12px 0 #21346D, 13px 13px 0 #182B64, 14px 14px 0 #0F225B, 15px 15px 0 #071A53, 16px 16px 0 #02114A, 17px 17px 0 #0B0841, 18px 18px 0 #130039, 19px 19px 0 #1C0930, 20px 20px 0 #251227, 21px 21px 20px rgba(0,0,0,1), 21px 21px 1px rgba(0,0,0,0.5), 0px 0px 20px rgba(0,0,0,.2);font-size: 50px;', }; diff --git a/packages/sdk/src/common/decorators/index.ts b/packages/sdk/src/common/decorators/index.ts index be355ca2..cf9bbd49 100644 --- a/packages/sdk/src/common/decorators/index.ts +++ b/packages/sdk/src/common/decorators/index.ts @@ -1,3 +1,4 @@ export { Cache } from './cache.js'; export { Logger } from './logger.js'; export { Initialize } from './Initialize.js'; +export { ErrorHandler } from './ErrorHandler.js'; diff --git a/packages/sdk/src/common/decorators/logger.ts b/packages/sdk/src/common/decorators/logger.ts index 1313cc0c..192601cb 100644 --- a/packages/sdk/src/common/decorators/logger.ts +++ b/packages/sdk/src/common/decorators/logger.ts @@ -1,13 +1,13 @@ -import { callConsoleMessage } from "./utils.js"; -import { HeadMessage } from "./types.js"; +import { callConsoleMessage } from './utils.js'; +import { HeadMessage } from './types.js'; -export const Logger = function (headMessage: HeadMessage = "LOG:") { +export const Logger = function (headMessage: HeadMessage = 'LOG:') { return function LoggerMethod( originalMethod: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext< This, (this: This, ...args: Args) => Return - > + >, ) { const methodName = String(context.name); @@ -24,8 +24,8 @@ export const Logger = function (headMessage: HeadMessage = "LOG:") { .catch((error) => { callConsoleMessage( headMessage, - `Exiting method '${methodName}' with error: ${error}.`, - "Error:" + `Exiting method '${methodName}' with error.`, + 'Error:', ); throw error; }) as Return; diff --git a/packages/sdk/src/common/decorators/types.ts b/packages/sdk/src/common/decorators/types.ts index d2b17981..82e5fc8e 100644 --- a/packages/sdk/src/common/decorators/types.ts +++ b/packages/sdk/src/common/decorators/types.ts @@ -1,11 +1,12 @@ export type HeadMessage = - | "Provider:" - | "Contracts:" - | "Balances:" - | "Utils:" - | "Views:" - | "Call:" - | "Error:" - | "LOG:" - | "Cache:" - | "Init:"; + | 'Provider:' + | 'Contracts:' + | 'Balances:' + | 'Utils:' + | 'Views:' + | 'Call:' + | 'Error:' + | 'LOG:' + | 'Cache:' + | 'Init:' + | 'Permit:'; diff --git a/packages/sdk/src/common/utils/index.ts b/packages/sdk/src/common/utils/index.ts index 3e7838e5..952d8e2a 100644 --- a/packages/sdk/src/common/utils/index.ts +++ b/packages/sdk/src/common/utils/index.ts @@ -1,4 +1,5 @@ -export { checkIsContract } from "./checkIsContract.js"; -export { getFeeData, type FeeData } from "./getFeeData.js"; -export { SDKError, type SDKErrorProps } from "./SDKError.js"; -export { getErrorMessage, type ErrorMessage } from "./getErrorMessage.js"; +export { checkIsContract } from './checkIsContract.js'; +export { getFeeData, type FeeData } from './getFeeData.js'; +export { SDKError, type SDKErrorProps } from './SDKError.js'; +export { getErrorMessage, type ErrorMessage } from './getErrorMessage.js'; +export { isBigint } from './isBigint.js'; diff --git a/packages/sdk/src/common/utils/isBigint.ts b/packages/sdk/src/common/utils/isBigint.ts new file mode 100644 index 00000000..a0673fa0 --- /dev/null +++ b/packages/sdk/src/common/utils/isBigint.ts @@ -0,0 +1,3 @@ +export const isBigint = (value: unknown): value is BigInt => { + return typeof value === 'bigint'; +}; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 144e0e66..74aa3878 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -2,8 +2,16 @@ export { LidoSDK } from './sdk.js'; export { type SDKError } from './common/utils/SDKError.js'; export * from './common/decorators/index.js'; export { - StakeCallbackStage, + StakeCallbackStages, StakeStageCallback, StakeProps, } from './staking/index.js'; +export { + RequestCallbackStages, + RequestStageCallback, + RequestProps, + RequestWithPermitProps, + ApproveCallbackStages, + ApproveStageCallback, +} from './withdrawals/index.js'; export { LIDO_CONTRACT_NAMES } from './common/constants.js'; diff --git a/packages/sdk/src/sdk.ts b/packages/sdk/src/sdk.ts index 49ce1387..fd762b67 100644 --- a/packages/sdk/src/sdk.ts +++ b/packages/sdk/src/sdk.ts @@ -1,16 +1,20 @@ -import { LidoSDKCore, LidoSDKCoreProps } from "./core/index.js"; -import { LidoSDKStaking } from "./staking/index.js"; +import { LidoSDKCore, LidoSDKCoreProps } from './core/index.js'; +import { LidoSDKStaking } from './staking/index.js'; +import { LidoSDKWithdrawals } from './withdrawals/index.js'; -import { version } from "./version.js"; +import { version } from './version.js'; export class LidoSDK { readonly core: LidoSDKCore; readonly staking: LidoSDKStaking; + readonly withdrawals: LidoSDKWithdrawals; constructor(props: LidoSDKCoreProps) { // Core functionality this.core = new LidoSDKCore(props, version); // Staking functionality this.staking = new LidoSDKStaking({ ...props, core: this.core }); + // Withdrawals functionality + this.withdrawals = new LidoSDKWithdrawals({ ...props, core: this.core }); } } diff --git a/packages/sdk/src/staking/abi/steth.ts b/packages/sdk/src/staking/abi/steth.ts index bdf181e3..cc385295 100644 --- a/packages/sdk/src/staking/abi/steth.ts +++ b/packages/sdk/src/staking/abi/steth.ts @@ -1,960 +1,894 @@ -export const abi = [ +export const StethAbi = [ { constant: false, inputs: [], - name: "resume", + name: 'resume', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "name", - outputs: [{ name: "", type: "string" }], + name: 'name', + outputs: [{ name: '', type: 'string' }], payable: false, - stateMutability: "pure", - type: "function", + stateMutability: 'pure', + type: 'function', }, { constant: false, inputs: [], - name: "stop", + name: 'stop', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "hasInitialized", - outputs: [{ name: "", type: "bool" }], + name: 'hasInitialized', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_spender", type: "address" }, - { name: "_amount", type: "uint256" }, + { name: '_spender', type: 'address' }, + { name: '_amount', type: 'uint256' }, ], - name: "approve", - outputs: [{ name: "", type: "bool" }], + name: 'approve', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "STAKING_CONTROL_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'STAKING_CONTROL_ROLE', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, - inputs: [ - { name: "_depositContract", type: "address" }, - { name: "_oracle", type: "address" }, - { name: "_operators", type: "address" }, - { name: "_treasury", type: "address" }, - { name: "_insuranceFund", type: "address" }, - ], - name: "initialize", - outputs: [], + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, - inputs: [], - name: "getInsuranceFund", - outputs: [{ name: "", type: "address" }], + inputs: [{ name: '_ethAmount', type: 'uint256' }], + name: 'getSharesByPooledEth', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "totalSupply", - outputs: [{ name: "", type: "uint256" }], + name: 'isStakingPaused', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: true, - inputs: [{ name: "_ethAmount", type: "uint256" }], - name: "getSharesByPooledEth", - outputs: [{ name: "", type: "uint256" }], + constant: false, + inputs: [ + { name: '_sender', type: 'address' }, + { name: '_recipient', type: 'address' }, + { name: '_amount', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, - inputs: [], - name: "isStakingPaused", - outputs: [{ name: "", type: "bool" }], + inputs: [{ name: '_script', type: 'bytes' }], + name: 'getEVMScriptExecutor', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_sender", type: "address" }, - { name: "_recipient", type: "address" }, - { name: "_amount", type: "uint256" }, + { name: '_maxStakeLimit', type: 'uint256' }, + { name: '_stakeLimitIncreasePerBlock', type: 'uint256' }, ], - name: "transferFrom", - outputs: [{ name: "", type: "bool" }], + name: 'setStakingLimit', + outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getOperators", - outputs: [{ name: "", type: "address" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [{ name: "_script", type: "bytes" }], - name: "getEVMScriptExecutor", - outputs: [{ name: "", type: "address" }], + name: 'RESUME_ROLE', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_maxStakeLimit", type: "uint256" }, - { name: "_stakeLimitIncreasePerBlock", type: "uint256" }, + { name: '_lidoLocator', type: 'address' }, + { name: '_eip712StETH', type: 'address' }, ], - name: "setStakingLimit", + name: 'finalizeUpgrade_v2', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", - }, - { - constant: true, - inputs: [], - name: "RESUME_ROLE", - outputs: [{ name: "", type: "bytes32" }], - payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "decimals", - outputs: [{ name: "", type: "uint8" }], + name: 'decimals', + outputs: [{ name: '', type: 'uint8' }], payable: false, - stateMutability: "pure", - type: "function", + stateMutability: 'pure', + type: 'function', }, { constant: true, inputs: [], - name: "getRecoveryVault", - outputs: [{ name: "", type: "address" }], + name: 'getRecoveryVault', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "DEPOSIT_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'DOMAIN_SEPARATOR', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "DEPOSIT_SIZE", - outputs: [{ name: "", type: "uint256" }], + name: 'getTotalPooledEther', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: true, - inputs: [], - name: "getTotalPooledEther", - outputs: [{ name: "", type: "uint256" }], + constant: false, + inputs: [{ name: '_newDepositedValidators', type: 'uint256' }], + name: 'unsafeChangeDepositedValidators', + outputs: [], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "PAUSE_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'PAUSE_ROLE', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_spender", type: "address" }, - { name: "_addedValue", type: "uint256" }, + { name: '_spender', type: 'address' }, + { name: '_addedValue', type: 'uint256' }, ], - name: "increaseAllowance", - outputs: [{ name: "", type: "bool" }], + name: 'increaseAllowance', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getTreasury", - outputs: [{ name: "", type: "address" }], + name: 'getTreasury', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "isStopped", - outputs: [{ name: "", type: "bool" }], + name: 'isStopped', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "MANAGE_WITHDRAWAL_KEY", - outputs: [{ name: "", type: "bytes32" }], + name: 'getBufferedEther', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [], - name: "getBufferedEther", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, - inputs: [], - name: "receiveELRewards", + inputs: [ + { name: '_lidoLocator', type: 'address' }, + { name: '_eip712StETH', type: 'address' }, + ], + name: 'initialize', outputs: [], payable: true, - stateMutability: "payable", - type: "function", + stateMutability: 'payable', + type: 'function', }, { - constant: true, + constant: false, inputs: [], - name: "getELRewardsWithdrawalLimit", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", + name: 'receiveELRewards', + outputs: [], + payable: true, + stateMutability: 'payable', + type: 'function', }, { constant: true, inputs: [], - name: "SIGNATURE_LENGTH", - outputs: [{ name: "", type: "uint256" }], + name: 'getWithdrawalCredentials', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getWithdrawalCredentials", - outputs: [{ name: "", type: "bytes32" }], + name: 'getCurrentStakeLimit', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getCurrentStakeLimit", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: false, - inputs: [{ name: "_limitPoints", type: "uint16" }], - name: "setELRewardsWithdrawalLimit", - outputs: [], + name: 'getStakeLimitFullInfo', + outputs: [ + { name: 'isStakingPaused', type: 'bool' }, + { name: 'isStakingLimitSet', type: 'bool' }, + { name: 'currentStakeLimit', type: 'uint256' }, + { name: 'maxStakeLimit', type: 'uint256' }, + { name: 'maxStakeLimitGrowthBlocks', type: 'uint256' }, + { name: 'prevStakeLimit', type: 'uint256' }, + { name: 'prevStakeBlockNumber', type: 'uint256' }, + ], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_beaconValidators", type: "uint256" }, - { name: "_beaconBalance", type: "uint256" }, + { name: '_sender', type: 'address' }, + { name: '_recipient', type: 'address' }, + { name: '_sharesAmount', type: 'uint256' }, ], - name: "handleOracleReport", - outputs: [], + name: 'transferSharesFrom', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, - inputs: [], - name: "getStakeLimitFullInfo", - outputs: [ - { name: "isStakingPaused", type: "bool" }, - { name: "isStakingLimitSet", type: "bool" }, - { name: "currentStakeLimit", type: "uint256" }, - { name: "maxStakeLimit", type: "uint256" }, - { name: "maxStakeLimitGrowthBlocks", type: "uint256" }, - { name: "prevStakeLimit", type: "uint256" }, - { name: "prevStakeBlockNumber", type: "uint256" }, - ], + inputs: [{ name: '_account', type: 'address' }], + name: 'balanceOf', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: true, + constant: false, inputs: [], - name: "SET_EL_REWARDS_WITHDRAWAL_LIMIT_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'resumeStaking', + outputs: [], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getELRewardsVault", - outputs: [{ name: "", type: "address" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [{ name: "_account", type: "address" }], - name: "balanceOf", - outputs: [{ name: "", type: "uint256" }], + name: 'getFeeDistribution', + outputs: [ + { name: 'treasuryFeeBasisPoints', type: 'uint16' }, + { name: 'insuranceFeeBasisPoints', type: 'uint16' }, + { name: 'operatorsFeeBasisPoints', type: 'uint16' }, + ], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [], - name: "resumeStaking", + name: 'receiveWithdrawals', outputs: [], - payable: false, - stateMutability: "nonpayable", - type: "function", + payable: true, + stateMutability: 'payable', + type: 'function', }, { constant: true, - inputs: [], - name: "getFeeDistribution", - outputs: [ - { name: "treasuryFeeBasisPoints", type: "uint16" }, - { name: "insuranceFeeBasisPoints", type: "uint16" }, - { name: "operatorsFeeBasisPoints", type: "uint16" }, - ], + inputs: [{ name: '_sharesAmount', type: 'uint256' }], + name: 'getPooledEthByShares', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, - inputs: [{ name: "_sharesAmount", type: "uint256" }], - name: "getPooledEthByShares", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: false, - inputs: [{ name: "_executionLayerRewardsVault", type: "address" }], - name: "setELRewardsVault", - outputs: [], + inputs: [{ name: 'token', type: 'address' }], + name: 'allowRecoverability', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, - inputs: [{ name: "token", type: "address" }], - name: "allowRecoverability", - outputs: [{ name: "", type: "bool" }], + inputs: [{ name: 'owner', type: 'address' }], + name: 'nonces', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "MANAGE_PROTOCOL_CONTRACTS_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'appId', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "appId", - outputs: [{ name: "", type: "bytes32" }], + name: 'getOracle', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getOracle", - outputs: [{ name: "", type: "address" }], + name: 'eip712Domain', + outputs: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getInitializationBlock", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: false, - inputs: [ - { name: "_treasuryFeeBasisPoints", type: "uint16" }, - { name: "_insuranceFeeBasisPoints", type: "uint16" }, - { name: "_operatorsFeeBasisPoints", type: "uint16" }, - ], - name: "setFeeDistribution", - outputs: [], + name: 'getContractVersion', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, - inputs: [{ name: "_feeBasisPoints", type: "uint16" }], - name: "setFee", - outputs: [], + constant: true, + inputs: [], + name: 'getInitializationBlock', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [ - { name: "_recipient", type: "address" }, - { name: "_sharesAmount", type: "uint256" }, + { name: '_recipient', type: 'address' }, + { name: '_sharesAmount', type: 'uint256' }, ], - name: "transferShares", - outputs: [{ name: "", type: "uint256" }], + name: 'transferShares', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", - }, - { - constant: false, - inputs: [{ name: "_maxDeposits", type: "uint256" }], - name: "depositBufferedEther", - outputs: [], - payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "symbol", - outputs: [{ name: "", type: "string" }], + name: 'symbol', + outputs: [{ name: '', type: 'string' }], payable: false, - stateMutability: "pure", - type: "function", + stateMutability: 'pure', + type: 'function', }, { constant: true, inputs: [], - name: "MANAGE_FEE", - outputs: [{ name: "", type: "bytes32" }], + name: 'getEIP712StETH', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, - inputs: [{ name: "_token", type: "address" }], - name: "transferToVault", + inputs: [{ name: '', type: 'address' }], + name: 'transferToVault', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [ - { name: "_sender", type: "address" }, - { name: "_role", type: "bytes32" }, - { name: "_params", type: "uint256[]" }, + { name: '_sender', type: 'address' }, + { name: '_role', type: 'bytes32' }, + { name: '_params', type: 'uint256[]' }, ], - name: "canPerform", - outputs: [{ name: "", type: "bool" }], + name: 'canPerform', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, - inputs: [{ name: "_referral", type: "address" }], - name: "submit", - outputs: [{ name: "", type: "uint256" }], + inputs: [{ name: '_referral', type: 'address' }], + name: 'submit', + outputs: [{ name: '', type: 'uint256' }], payable: true, - stateMutability: "payable", - type: "function", - }, - { - constant: true, - inputs: [], - name: "WITHDRAWAL_CREDENTIALS_LENGTH", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'payable', + type: 'function', }, { constant: false, inputs: [ - { name: "_spender", type: "address" }, - { name: "_subtractedValue", type: "uint256" }, + { name: '_spender', type: 'address' }, + { name: '_subtractedValue', type: 'uint256' }, ], - name: "decreaseAllowance", - outputs: [{ name: "", type: "bool" }], + name: 'decreaseAllowance', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getEVMScriptRegistry", - outputs: [{ name: "", type: "address" }], + name: 'getEVMScriptRegistry', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: true, - inputs: [], - name: "PUBKEY_LENGTH", - outputs: [{ name: "", type: "uint256" }], - payable: false, - stateMutability: "view", - type: "function", - }, - { - constant: true, - inputs: [], - name: "SET_EL_REWARDS_VAULT_ROLE", - outputs: [{ name: "", type: "bytes32" }], + constant: false, + inputs: [ + { name: '_recipient', type: 'address' }, + { name: '_amount', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: false, inputs: [ - { name: "_recipient", type: "address" }, - { name: "_amount", type: "uint256" }, + { name: '_maxDepositsCount', type: 'uint256' }, + { name: '_stakingModuleId', type: 'uint256' }, + { name: '_depositCalldata', type: 'bytes' }, ], - name: "transfer", - outputs: [{ name: "", type: "bool" }], + name: 'deposit', + outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getDepositContract", - outputs: [{ name: "", type: "address" }], + name: 'UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getBeaconStat", + name: 'getBeaconStat', outputs: [ - { name: "depositedValidators", type: "uint256" }, - { name: "beaconValidators", type: "uint256" }, - { name: "beaconBalance", type: "uint256" }, + { name: 'depositedValidators', type: 'uint256' }, + { name: 'beaconValidators', type: 'uint256' }, + { name: 'beaconBalance', type: 'uint256' }, ], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [], - name: "removeStakingLimit", + name: 'removeStakingLimit', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { - constant: true, - inputs: [], - name: "BURN_ROLE", - outputs: [{ name: "", type: "bytes32" }], + constant: false, + inputs: [ + { name: '_reportTimestamp', type: 'uint256' }, + { name: '_timeElapsed', type: 'uint256' }, + { name: '_clValidators', type: 'uint256' }, + { name: '_clBalance', type: 'uint256' }, + { name: '_withdrawalVaultBalance', type: 'uint256' }, + { name: '_elRewardsVaultBalance', type: 'uint256' }, + { name: '_sharesRequestedToBurn', type: 'uint256' }, + { name: '_withdrawalFinalizationBatches', type: 'uint256[]' }, + { name: '_simulatedShareRate', type: 'uint256' }, + ], + name: 'handleOracleReport', + outputs: [{ name: 'postRebaseAmounts', type: 'uint256[4]' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getFee", - outputs: [{ name: "feeBasisPoints", type: "uint16" }], + name: 'getFee', + outputs: [{ name: 'totalFee', type: 'uint16' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "kernel", - outputs: [{ name: "", type: "address" }], + name: 'kernel', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "getTotalShares", - outputs: [{ name: "", type: "uint256" }], + name: 'getTotalShares', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: true, + constant: false, inputs: [ - { name: "_owner", type: "address" }, - { name: "_spender", type: "address" }, + { name: '_owner', type: 'address' }, + { name: '_spender', type: 'address' }, + { name: '_value', type: 'uint256' }, + { name: '_deadline', type: 'uint256' }, + { name: '_v', type: 'uint8' }, + { name: '_r', type: 'bytes32' }, + { name: '_s', type: 'bytes32' }, ], - name: "allowance", - outputs: [{ name: "", type: "uint256" }], + name: 'permit', + outputs: [], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, - inputs: [], - name: "isPetrified", - outputs: [{ name: "", type: "bool" }], + inputs: [ + { name: '_owner', type: 'address' }, + { name: '_spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, - inputs: [ - { name: "_oracle", type: "address" }, - { name: "_treasury", type: "address" }, - { name: "_insuranceFund", type: "address" }, - ], - name: "setProtocolContracts", - outputs: [], + constant: true, + inputs: [], + name: 'isPetrified', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, - inputs: [{ name: "_withdrawalCredentials", type: "bytes32" }], - name: "setWithdrawalCredentials", - outputs: [], + constant: true, + inputs: [], + name: 'getLidoLocator', + outputs: [{ name: '', type: 'address' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, inputs: [], - name: "STAKING_PAUSE_ROLE", - outputs: [{ name: "", type: "bytes32" }], + name: 'canDeposit', + outputs: [{ name: '', type: 'bool' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, + constant: true, inputs: [], - name: "depositBufferedEther", - outputs: [], + name: 'STAKING_PAUSE_ROLE', + outputs: [{ name: '', type: 'bytes32' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { - constant: false, - inputs: [ - { name: "_account", type: "address" }, - { name: "_sharesAmount", type: "uint256" }, - ], - name: "burnShares", - outputs: [{ name: "newTotalShares", type: "uint256" }], + constant: true, + inputs: [], + name: 'getDepositableEther', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: true, - inputs: [{ name: "_account", type: "address" }], - name: "sharesOf", - outputs: [{ name: "", type: "uint256" }], + inputs: [{ name: '_account', type: 'address' }], + name: 'sharesOf', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, { constant: false, inputs: [], - name: "pauseStaking", + name: 'pauseStaking', outputs: [], payable: false, - stateMutability: "nonpayable", - type: "function", + stateMutability: 'nonpayable', + type: 'function', }, { constant: true, inputs: [], - name: "getTotalELRewardsCollected", - outputs: [{ name: "", type: "uint256" }], + name: 'getTotalELRewardsCollected', + outputs: [{ name: '', type: 'uint256' }], payable: false, - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, - { payable: true, stateMutability: "payable", type: "fallback" }, + { payable: true, stateMutability: 'payable', type: 'fallback' }, { anonymous: false, - inputs: [ - { indexed: true, name: "executor", type: "address" }, - { indexed: false, name: "script", type: "bytes" }, - { indexed: false, name: "input", type: "bytes" }, - { indexed: false, name: "returnData", type: "bytes" }, - ], - name: "ScriptResult", - type: "event", + inputs: [], + name: 'StakingPaused', + type: 'event', }, { anonymous: false, - inputs: [ - { indexed: true, name: "vault", type: "address" }, - { indexed: true, name: "token", type: "address" }, - { indexed: false, name: "amount", type: "uint256" }, - ], - name: "RecoverToVault", - type: "event", + inputs: [], + name: 'StakingResumed', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: true, name: "from", type: "address" }, - { indexed: true, name: "to", type: "address" }, - { indexed: false, name: "sharesValue", type: "uint256" }, + { indexed: false, name: 'maxStakeLimit', type: 'uint256' }, + { + indexed: false, + name: 'stakeLimitIncreasePerBlock', + type: 'uint256', + }, ], - name: "TransferShares", - type: "event", + name: 'StakingLimitSet', + type: 'event', }, { anonymous: false, - inputs: [ - { indexed: true, name: "account", type: "address" }, - { indexed: false, name: "preRebaseTokenAmount", type: "uint256" }, - { indexed: false, name: "postRebaseTokenAmount", type: "uint256" }, - { indexed: false, name: "sharesAmount", type: "uint256" }, - ], - name: "SharesBurnt", - type: "event", + inputs: [], + name: 'StakingLimitRemoved', + type: 'event', }, - { anonymous: false, inputs: [], name: "Stopped", type: "event" }, - { anonymous: false, inputs: [], name: "Resumed", type: "event" }, { anonymous: false, inputs: [ - { indexed: true, name: "from", type: "address" }, - { indexed: true, name: "to", type: "address" }, - { indexed: false, name: "value", type: "uint256" }, + { indexed: true, name: 'reportTimestamp', type: 'uint256' }, + { indexed: false, name: 'preCLValidators', type: 'uint256' }, + { indexed: false, name: 'postCLValidators', type: 'uint256' }, ], - name: "Transfer", - type: "event", + name: 'CLValidatorsUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: 'depositedValidators', type: 'uint256' }], + name: 'DepositedValidatorsChanged', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: true, name: "owner", type: "address" }, - { indexed: true, name: "spender", type: "address" }, - { indexed: false, name: "value", type: "uint256" }, + { indexed: true, name: 'reportTimestamp', type: 'uint256' }, + { indexed: false, name: 'preCLBalance', type: 'uint256' }, + { indexed: false, name: 'postCLBalance', type: 'uint256' }, + { indexed: false, name: 'withdrawalsWithdrawn', type: 'uint256' }, + { + indexed: false, + name: 'executionLayerRewardsWithdrawn', + type: 'uint256', + }, + { indexed: false, name: 'postBufferedEther', type: 'uint256' }, ], - name: "Approval", - type: "event", + name: 'ETHDistributed', + type: 'event', }, { anonymous: false, - inputs: [], - name: "StakingPaused", - type: "event", + inputs: [ + { indexed: true, name: 'reportTimestamp', type: 'uint256' }, + { indexed: false, name: 'timeElapsed', type: 'uint256' }, + { indexed: false, name: 'preTotalShares', type: 'uint256' }, + { indexed: false, name: 'preTotalEther', type: 'uint256' }, + { indexed: false, name: 'postTotalShares', type: 'uint256' }, + { indexed: false, name: 'postTotalEther', type: 'uint256' }, + { indexed: false, name: 'sharesMintedAsFees', type: 'uint256' }, + ], + name: 'TokenRebased', + type: 'event', }, { anonymous: false, - inputs: [], - name: "StakingResumed", - type: "event", + inputs: [{ indexed: false, name: 'lidoLocator', type: 'address' }], + name: 'LidoLocatorSet', + type: 'event', }, { anonymous: false, - inputs: [ - { indexed: false, name: "maxStakeLimit", type: "uint256" }, - { - indexed: false, - name: "stakeLimitIncreasePerBlock", - type: "uint256", - }, - ], - name: "StakingLimitSet", - type: "event", + inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], + name: 'ELRewardsReceived', + type: 'event', }, { anonymous: false, - inputs: [], - name: "StakingLimitRemoved", - type: "event", + inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], + name: 'WithdrawalsReceived', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: false, name: "oracle", type: "address" }, - { indexed: false, name: "treasury", type: "address" }, - { indexed: false, name: "insuranceFund", type: "address" }, + { indexed: true, name: 'sender', type: 'address' }, + { indexed: false, name: 'amount', type: 'uint256' }, + { indexed: false, name: 'referral', type: 'address' }, ], - name: "ProtocolContactsSet", - type: "event", + name: 'Submitted', + type: 'event', }, { anonymous: false, - inputs: [{ indexed: false, name: "feeBasisPoints", type: "uint16" }], - name: "FeeSet", - type: "event", + inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], + name: 'Unbuffered', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: false, name: "treasuryFeeBasisPoints", type: "uint16" }, - { indexed: false, name: "insuranceFeeBasisPoints", type: "uint16" }, - { indexed: false, name: "operatorsFeeBasisPoints", type: "uint16" }, + { indexed: true, name: 'executor', type: 'address' }, + { indexed: false, name: 'script', type: 'bytes' }, + { indexed: false, name: 'input', type: 'bytes' }, + { indexed: false, name: 'returnData', type: 'bytes' }, ], - name: "FeeDistributionSet", - type: "event", + name: 'ScriptResult', + type: 'event', }, { anonymous: false, - inputs: [{ indexed: false, name: "amount", type: "uint256" }], - name: "ELRewardsReceived", - type: "event", + inputs: [ + { indexed: true, name: 'vault', type: 'address' }, + { indexed: true, name: 'token', type: 'address' }, + { indexed: false, name: 'amount', type: 'uint256' }, + ], + name: 'RecoverToVault', + type: 'event', }, { anonymous: false, - inputs: [{ indexed: false, name: "limitPoints", type: "uint256" }], - name: "ELRewardsWithdrawalLimitSet", - type: "event", + inputs: [{ indexed: false, name: 'eip712StETH', type: 'address' }], + name: 'EIP712StETHInitialized', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: false, name: "withdrawalCredentials", type: "bytes32" }, + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'sharesValue', type: 'uint256' }, ], - name: "WithdrawalCredentialsSet", - type: "event", + name: 'TransferShares', + type: 'event', }, { anonymous: false, inputs: [ - { - indexed: false, - name: "executionLayerRewardsVault", - type: "address", - }, + { indexed: true, name: 'account', type: 'address' }, + { indexed: false, name: 'preRebaseTokenAmount', type: 'uint256' }, + { indexed: false, name: 'postRebaseTokenAmount', type: 'uint256' }, + { indexed: false, name: 'sharesAmount', type: 'uint256' }, ], - name: "ELRewardsVaultSet", - type: "event", + name: 'SharesBurnt', + type: 'event', }, + { anonymous: false, inputs: [], name: 'Stopped', type: 'event' }, + { anonymous: false, inputs: [], name: 'Resumed', type: 'event' }, { anonymous: false, inputs: [ - { indexed: true, name: "sender", type: "address" }, - { indexed: false, name: "amount", type: "uint256" }, - { indexed: false, name: "referral", type: "address" }, + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, ], - name: "Submitted", - type: "event", + name: 'Transfer', + type: 'event', }, { anonymous: false, inputs: [ - { indexed: true, name: "sender", type: "address" }, - { indexed: false, name: "tokenAmount", type: "uint256" }, - { indexed: false, name: "sentFromBuffer", type: "uint256" }, - { indexed: true, name: "pubkeyHash", type: "bytes32" }, - { indexed: false, name: "etherAmount", type: "uint256" }, + { indexed: true, name: 'owner', type: 'address' }, + { indexed: true, name: 'spender', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, ], - name: "Withdrawal", - type: "event", + name: 'Approval', + type: 'event', }, { anonymous: false, - inputs: [{ indexed: false, name: "amount", type: "uint256" }], - name: "Unbuffered", - type: "event", + inputs: [{ indexed: false, name: 'version', type: 'uint256' }], + name: 'ContractVersionSet', + type: 'event', }, ] as const; diff --git a/packages/sdk/src/staking/index.ts b/packages/sdk/src/staking/index.ts index 17f314e4..e4d987ae 100644 --- a/packages/sdk/src/staking/index.ts +++ b/packages/sdk/src/staking/index.ts @@ -1,2 +1,6 @@ -export { LidoSDKStaking } from "./staking.js"; -export { StakeCallbackStage, StakeStageCallback, StakeProps } from "./types.js"; +export { LidoSDKStaking } from './staking.js'; +export { + StakeCallbackStages, + StakeStageCallback, + StakeProps, +} from './types.js'; diff --git a/packages/sdk/src/staking/staking.ts b/packages/sdk/src/staking/staking.ts index 4140eb58..7eb86635 100644 --- a/packages/sdk/src/staking/staking.ts +++ b/packages/sdk/src/staking/staking.ts @@ -1,8 +1,5 @@ +import { zeroAddress, parseEther, getContract, encodeFunctionData } from 'viem'; import { - zeroAddress, - parseEther, - getContract, - encodeFunctionData, type Address, type Account, type GetContractReturnType, @@ -22,10 +19,10 @@ import { } from '../common/constants.js'; import { version } from '../version.js'; -import { abi } from './abi/steth.js'; +import { StethAbi } from './abi/steth.js'; import { LidoSDKStakingProps, - StakeCallbackStage, + StakeCallbackStages, StakeProps, StakeResult, StakeEncodeDataProps, @@ -64,13 +61,13 @@ export class LidoSDKStaking { @Logger('Contracts:') @Cache(30 * 60 * 1000, ['core.chain.id', 'contractAddressStETH']) public async getContractStETH(): Promise< - GetContractReturnType + GetContractReturnType > { const address = await this.contractAddressStETH(); return getContract({ address, - abi: abi, + abi: StethAbi, publicClient: this.core.rpcProvider, walletClient: this.core.web3Provider, }); @@ -95,7 +92,7 @@ export class LidoSDKStaking { error, code, }); - callback?.({ stage: StakeCallbackStage.ERROR, payload: txError }); + callback?.({ stage: StakeCallbackStages.ERROR, payload: txError }); throw txError; } @@ -116,7 +113,7 @@ export class LidoSDKStaking { referralAddress, ); - callback?.({ stage: StakeCallbackStage.SIGN }); + callback?.({ stage: StakeCallbackStages.SIGN }); const contract = await this.getContractStETH(); const transaction = await contract.write.submit([referralAddress], { @@ -127,7 +124,7 @@ export class LidoSDKStaking { account, }); - callback?.({ stage: StakeCallbackStage.RECEIPT, payload: transaction }); + callback?.({ stage: StakeCallbackStages.RECEIPT, payload: transaction }); const transactionReceipt = await this.core.rpcProvider.waitForTransactionReceipt({ @@ -135,7 +132,7 @@ export class LidoSDKStaking { }); callback?.({ - stage: StakeCallbackStage.CONFIRMATION, + stage: StakeCallbackStages.CONFIRMATION, payload: transactionReceipt, }); @@ -144,7 +141,7 @@ export class LidoSDKStaking { hash: transactionReceipt.transactionHash, }); - callback?.({ stage: StakeCallbackStage.DONE, payload: confirmations }); + callback?.({ stage: StakeCallbackStages.DONE, payload: confirmations }); return { hash: transaction, receipt: transactionReceipt, confirmations }; } @@ -153,7 +150,7 @@ export class LidoSDKStaking { private async stakeMultisig(props: StakeProps): Promise { const { value, callback, referralAddress = zeroAddress, account } = props; - callback?.({ stage: StakeCallbackStage.SIGN }); + callback?.({ stage: StakeCallbackStages.SIGN }); const contract = await this.getContractStETH(); const transaction = await contract.write.submit([referralAddress], { @@ -162,7 +159,7 @@ export class LidoSDKStaking { account, }); - callback?.({ stage: StakeCallbackStage.MULTISIG_DONE }); + callback?.({ stage: StakeCallbackStages.MULTISIG_DONE }); return { hash: transaction }; } @@ -176,7 +173,7 @@ export class LidoSDKStaking { const address = await this.contractAddressStETH(); const { request } = await this.core.rpcProvider.simulateContract({ address, - abi, + abi: StethAbi, functionName: 'submit', account, args: [referralAddress], @@ -257,7 +254,7 @@ export class LidoSDKStaking { const { referralAddress = zeroAddress } = props; return encodeFunctionData({ - abi, + abi: StethAbi, functionName: 'submit', args: [referralAddress], }); diff --git a/packages/sdk/src/staking/types.ts b/packages/sdk/src/staking/types.ts index 768ce003..ed135c17 100644 --- a/packages/sdk/src/staking/types.ts +++ b/packages/sdk/src/staking/types.ts @@ -1,27 +1,27 @@ -import { type Address, type Hash, type TransactionReceipt } from "viem"; -import { type LidoSDKCoreProps, type LidoSDKCore } from "../core/index.js"; -import { type SDKError } from "../common/utils/index.js"; +import { type Address, type Hash, type TransactionReceipt } from 'viem'; +import { type LidoSDKCoreProps, type LidoSDKCore } from '../core/index.js'; +import { type SDKError } from '../common/utils/index.js'; export type LidoSDKStakingProps = LidoSDKCoreProps & { core?: LidoSDKCore; }; -export enum StakeCallbackStage { - "SIGN" = "sign", - "RECEIPT" = "receipt", - "CONFIRMATION" = "confirmation", - "DONE" = "done", - "MULTISIG_DONE" = "multisig_done", - "ERROR" = "error", +export enum StakeCallbackStages { + 'SIGN' = 'sign', + 'RECEIPT' = 'receipt', + 'CONFIRMATION' = 'confirmation', + 'DONE' = 'done', + 'MULTISIG_DONE' = 'multisig_done', + 'ERROR' = 'error', } export type StakeCallbackProps = - | { stage: StakeCallbackStage.SIGN; payload?: undefined } - | { stage: StakeCallbackStage.RECEIPT; payload: Hash } - | { stage: StakeCallbackStage.CONFIRMATION; payload: TransactionReceipt } - | { stage: StakeCallbackStage.DONE; payload: bigint } - | { stage: StakeCallbackStage.MULTISIG_DONE; payload?: undefined } - | { stage: StakeCallbackStage.ERROR; payload: SDKError }; + | { stage: StakeCallbackStages.SIGN; payload?: undefined } + | { stage: StakeCallbackStages.RECEIPT; payload: Hash } + | { stage: StakeCallbackStages.CONFIRMATION; payload: TransactionReceipt } + | { stage: StakeCallbackStages.DONE; payload: bigint } + | { stage: StakeCallbackStages.MULTISIG_DONE; payload?: undefined } + | { stage: StakeCallbackStages.ERROR; payload: SDKError }; export type StakeStageCallback = (props: StakeCallbackProps) => void; diff --git a/packages/sdk/src/withdrawals/abi/partStETH.ts b/packages/sdk/src/withdrawals/abi/partStETH.ts new file mode 100644 index 00000000..d8aa9707 --- /dev/null +++ b/packages/sdk/src/withdrawals/abi/partStETH.ts @@ -0,0 +1,58 @@ +export const PartStethAbi = [ + { + constant: true, + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [{ name: '', type: 'bytes32' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'eip712Domain', + outputs: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [{ name: 'owner', type: 'address' }], + name: 'nonces', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_spender', type: 'address' }, + { name: '_amount', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [ + { name: '_owner', type: 'address' }, + { name: '_spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, +] as const; diff --git a/packages/sdk/src/withdrawals/abi/partWstETH.ts b/packages/sdk/src/withdrawals/abi/partWstETH.ts new file mode 100644 index 00000000..3bb63027 --- /dev/null +++ b/packages/sdk/src/withdrawals/abi/partWstETH.ts @@ -0,0 +1,36 @@ +export const PartWstethAbi = [ + { + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'owner', type: 'address' }], + name: 'nonces', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'spender', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'address', name: 'spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const; diff --git a/packages/sdk/src/withdrawals/abi/withdrawalsQueue.ts b/packages/sdk/src/withdrawals/abi/withdrawalsQueue.ts new file mode 100644 index 00000000..442ae90e --- /dev/null +++ b/packages/sdk/src/withdrawals/abi/withdrawalsQueue.ts @@ -0,0 +1,1122 @@ +export const WithdrawalsQueueAbi = [ + { inputs: [], name: 'AdminZeroAddress', type: 'error' }, + { inputs: [], name: 'ApprovalToOwner', type: 'error' }, + { inputs: [], name: 'ApproveToCaller', type: 'error' }, + { + inputs: [ + { internalType: 'uint256', name: '_firstArrayLength', type: 'uint256' }, + { internalType: 'uint256', name: '_secondArrayLength', type: 'uint256' }, + ], + name: 'ArraysLengthMismatch', + type: 'error', + }, + { inputs: [], name: 'BatchesAreNotSorted', type: 'error' }, + { inputs: [], name: 'CantSendValueRecipientMayHaveReverted', type: 'error' }, + { inputs: [], name: 'EmptyBatches', type: 'error' }, + { inputs: [], name: 'InvalidContractVersionIncrement', type: 'error' }, + { + inputs: [{ internalType: 'uint256', name: '_hint', type: 'uint256' }], + name: 'InvalidHint', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'InvalidOwnerAddress', + type: 'error', + }, + { inputs: [], name: 'InvalidReportTimestamp', type: 'error' }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'InvalidRequestId', + type: 'error', + }, + { + inputs: [ + { internalType: 'uint256', name: 'startId', type: 'uint256' }, + { internalType: 'uint256', name: 'endId', type: 'uint256' }, + ], + name: 'InvalidRequestIdRange', + type: 'error', + }, + { inputs: [], name: 'InvalidState', type: 'error' }, + { inputs: [], name: 'NonZeroContractVersionOnInit', type: 'error' }, + { inputs: [], name: 'NotEnoughEther', type: 'error' }, + { + inputs: [ + { internalType: 'address', name: '_sender', type: 'address' }, + { internalType: 'address', name: '_owner', type: 'address' }, + ], + name: 'NotOwner', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'sender', type: 'address' }], + name: 'NotOwnerOrApproved', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'sender', type: 'address' }], + name: 'NotOwnerOrApprovedForAll', + type: 'error', + }, + { inputs: [], name: 'PauseUntilMustBeInFuture', type: 'error' }, + { inputs: [], name: 'PausedExpected', type: 'error' }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'RequestAlreadyClaimed', + type: 'error', + }, + { + inputs: [ + { internalType: 'uint256', name: '_amountOfStETH', type: 'uint256' }, + ], + name: 'RequestAmountTooLarge', + type: 'error', + }, + { + inputs: [ + { internalType: 'uint256', name: '_amountOfStETH', type: 'uint256' }, + ], + name: 'RequestAmountTooSmall', + type: 'error', + }, + { inputs: [], name: 'RequestIdsNotSorted', type: 'error' }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'RequestNotFoundOrNotFinalized', + type: 'error', + }, + { inputs: [], name: 'ResumedExpected', type: 'error' }, + { + inputs: [{ internalType: 'string', name: 'str', type: 'string' }], + name: 'StringTooLong', + type: 'error', + }, + { + inputs: [ + { internalType: 'uint256', name: 'sent', type: 'uint256' }, + { internalType: 'uint256', name: 'maxExpected', type: 'uint256' }, + ], + name: 'TooMuchEtherToFinalize', + type: 'error', + }, + { + inputs: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'realOwner', type: 'address' }, + ], + name: 'TransferFromIncorrectOwner', + type: 'error', + }, + { inputs: [], name: 'TransferFromZeroAddress', type: 'error' }, + { + inputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'TransferToNonIERC721Receiver', + type: 'error', + }, + { inputs: [], name: 'TransferToThemselves', type: 'error' }, + { inputs: [], name: 'TransferToZeroAddress', type: 'error' }, + { + inputs: [ + { internalType: 'uint256', name: 'expected', type: 'uint256' }, + { internalType: 'uint256', name: 'received', type: 'uint256' }, + ], + name: 'UnexpectedContractVersion', + type: 'error', + }, + { inputs: [], name: 'ZeroAmountOfETH', type: 'error' }, + { inputs: [], name: 'ZeroMetadata', type: 'error' }, + { inputs: [], name: 'ZeroPauseDuration', type: 'error' }, + { inputs: [], name: 'ZeroRecipient', type: 'error' }, + { inputs: [], name: 'ZeroShareRate', type: 'error' }, + { inputs: [], name: 'ZeroTimestamp', type: 'error' }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'approved', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'operator', + type: 'address', + }, + { indexed: false, internalType: 'bool', name: 'approved', type: 'bool' }, + ], + name: 'ApprovalForAll', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'string', + name: 'baseURI', + type: 'string', + }, + ], + name: 'BaseURISet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: '_fromTokenId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: '_toTokenId', + type: 'uint256', + }, + ], + name: 'BatchMetadataUpdate', + type: 'event', + }, + { anonymous: false, inputs: [], name: 'BunkerModeDisabled', type: 'event' }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: '_sinceTimestamp', + type: 'uint256', + }, + ], + name: 'BunkerModeEnabled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'version', + type: 'uint256', + }, + ], + name: 'ContractVersionSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: '_admin', + type: 'address', + }, + ], + name: 'InitializedV1', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: '_tokenId', + type: 'uint256', + }, + ], + name: 'MetadataUpdate', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'nftDescriptorAddress', + type: 'address', + }, + ], + name: 'NftDescriptorAddressSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'duration', + type: 'uint256', + }, + ], + name: 'Paused', + type: 'event', + }, + { anonymous: false, inputs: [], name: 'Resumed', type: 'event' }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'bytes32', + name: 'previousAdminRole', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'newAdminRole', + type: 'bytes32', + }, + ], + name: 'RoleAdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'requestId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'receiver', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOfETH', + type: 'uint256', + }, + ], + name: 'WithdrawalClaimed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'requestId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'address', + name: 'requestor', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOfStETH', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOfShares', + type: 'uint256', + }, + ], + name: 'WithdrawalRequested', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'uint256', name: 'from', type: 'uint256' }, + { indexed: true, internalType: 'uint256', name: 'to', type: 'uint256' }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOfETHLocked', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'sharesToBurn', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'timestamp', + type: 'uint256', + }, + ], + name: 'WithdrawalsFinalized', + type: 'event', + }, + { + inputs: [], + name: 'BUNKER_MODE_DISABLED_TIMESTAMP', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'DEFAULT_ADMIN_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'FINALIZE_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MANAGE_TOKEN_URI_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_BATCHES_LENGTH', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_STETH_WITHDRAWAL_AMOUNT', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MIN_STETH_WITHDRAWAL_AMOUNT', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'ORACLE_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PAUSE_INFINITELY', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PAUSE_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'RESUME_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'STETH', + outputs: [{ internalType: 'contract IStETH', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'WSTETH', + outputs: [{ internalType: 'contract IWstETH', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_requestId', type: 'uint256' }, + ], + name: 'approve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_owner', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'bunkerModeSinceTimestamp', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: '_maxShareRate', type: 'uint256' }, + { internalType: 'uint256', name: '_maxTimestamp', type: 'uint256' }, + { internalType: 'uint256', name: '_maxRequestsPerCall', type: 'uint256' }, + { + components: [ + { + internalType: 'uint256', + name: 'remainingEthBudget', + type: 'uint256', + }, + { internalType: 'bool', name: 'finished', type: 'bool' }, + { internalType: 'uint256[36]', name: 'batches', type: 'uint256[36]' }, + { internalType: 'uint256', name: 'batchesLength', type: 'uint256' }, + ], + internalType: 'struct WithdrawalQueueBase.BatchesCalculationState', + name: '_state', + type: 'tuple', + }, + ], + name: 'calculateFinalizationBatches', + outputs: [ + { + components: [ + { + internalType: 'uint256', + name: 'remainingEthBudget', + type: 'uint256', + }, + { internalType: 'bool', name: 'finished', type: 'bool' }, + { internalType: 'uint256[36]', name: 'batches', type: 'uint256[36]' }, + { internalType: 'uint256', name: 'batchesLength', type: 'uint256' }, + ], + internalType: 'struct WithdrawalQueueBase.BatchesCalculationState', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'claimWithdrawal', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_requestIds', type: 'uint256[]' }, + { internalType: 'uint256[]', name: '_hints', type: 'uint256[]' }, + ], + name: 'claimWithdrawals', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_requestIds', type: 'uint256[]' }, + { internalType: 'uint256[]', name: '_hints', type: 'uint256[]' }, + { internalType: 'address', name: '_recipient', type: 'address' }, + ], + name: 'claimWithdrawalsTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_lastRequestIdToBeFinalized', + type: 'uint256', + }, + { internalType: 'uint256', name: '_maxShareRate', type: 'uint256' }, + ], + name: 'finalize', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_requestIds', type: 'uint256[]' }, + { internalType: 'uint256', name: '_firstIndex', type: 'uint256' }, + { internalType: 'uint256', name: '_lastIndex', type: 'uint256' }, + ], + name: 'findCheckpointHints', + outputs: [ + { internalType: 'uint256[]', name: 'hintIds', type: 'uint256[]' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'getApproved', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getBaseURI', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_requestIds', type: 'uint256[]' }, + { internalType: 'uint256[]', name: '_hints', type: 'uint256[]' }, + ], + name: 'getClaimableEther', + outputs: [ + { + internalType: 'uint256[]', + name: 'claimableEthValues', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getContractVersion', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLastCheckpointIndex', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLastFinalizedRequestId', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLastRequestId', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLockedEtherAmount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNFTDescriptorAddress', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getResumeSinceTimestamp', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: 'role', type: 'bytes32' }], + name: 'getRoleAdmin', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'uint256', name: 'index', type: 'uint256' }, + ], + name: 'getRoleMember', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: 'role', type: 'bytes32' }], + name: 'getRoleMemberCount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_owner', type: 'address' }], + name: 'getWithdrawalRequests', + outputs: [ + { internalType: 'uint256[]', name: 'requestsIds', type: 'uint256[]' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_requestIds', type: 'uint256[]' }, + ], + name: 'getWithdrawalStatus', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'amountOfStETH', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOfShares', type: 'uint256' }, + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'uint256', name: 'timestamp', type: 'uint256' }, + { internalType: 'bool', name: 'isFinalized', type: 'bool' }, + { internalType: 'bool', name: 'isClaimed', type: 'bool' }, + ], + internalType: 'struct WithdrawalQueueBase.WithdrawalRequestStatus[]', + name: 'statuses', + type: 'tuple[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'grantRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'hasRole', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '_admin', type: 'address' }], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_owner', type: 'address' }, + { internalType: 'address', name: '_operator', type: 'address' }, + ], + name: 'isApprovedForAll', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isBunkerModeActive', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bool', name: '_isBunkerModeNow', type: 'bool' }, + { + internalType: 'uint256', + name: '_bunkerStartTimestamp', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '_currentReportTimestamp', + type: 'uint256', + }, + ], + name: 'onOracleReport', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'ownerOf', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_duration', type: 'uint256' }], + name: 'pauseFor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_pauseUntilInclusive', + type: 'uint256', + }, + ], + name: 'pauseUntil', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_batches', type: 'uint256[]' }, + { internalType: 'uint256', name: '_maxShareRate', type: 'uint256' }, + ], + name: 'prefinalize', + outputs: [ + { internalType: 'uint256', name: 'ethToLock', type: 'uint256' }, + { internalType: 'uint256', name: 'sharesToBurn', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'renounceRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_amounts', type: 'uint256[]' }, + { internalType: 'address', name: '_owner', type: 'address' }, + ], + name: 'requestWithdrawals', + outputs: [ + { internalType: 'uint256[]', name: 'requestIds', type: 'uint256[]' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_amounts', type: 'uint256[]' }, + { internalType: 'address', name: '_owner', type: 'address' }, + { + components: [ + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' }, + ], + internalType: 'struct WithdrawalQueue.PermitInput', + name: '_permit', + type: 'tuple', + }, + ], + name: 'requestWithdrawalsWithPermit', + outputs: [ + { internalType: 'uint256[]', name: 'requestIds', type: 'uint256[]' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_amounts', type: 'uint256[]' }, + { internalType: 'address', name: '_owner', type: 'address' }, + ], + name: 'requestWithdrawalsWstETH', + outputs: [ + { internalType: 'uint256[]', name: 'requestIds', type: 'uint256[]' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256[]', name: '_amounts', type: 'uint256[]' }, + { internalType: 'address', name: '_owner', type: 'address' }, + { + components: [ + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' }, + ], + internalType: 'struct WithdrawalQueue.PermitInput', + name: '_permit', + type: 'tuple', + }, + ], + name: 'requestWithdrawalsWstETHWithPermit', + outputs: [ + { internalType: 'uint256[]', name: 'requestIds', type: 'uint256[]' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'resume', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'revokeRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_from', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_requestId', type: 'uint256' }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_from', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_requestId', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_operator', type: 'address' }, + { internalType: 'bool', name: '_approved', type: 'bool' }, + ], + name: 'setApprovalForAll', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'string', name: '_baseURI', type: 'string' }], + name: 'setBaseURI', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_nftDescriptorAddress', + type: 'address', + }, + ], + name: 'setNFTDescriptorAddress', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }], + name: 'supportsInterface', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '_requestId', type: 'uint256' }], + name: 'tokenURI', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '_from', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_requestId', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unfinalizedRequestNumber', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'unfinalizedStETH', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const; diff --git a/packages/sdk/src/withdrawals/bus.ts b/packages/sdk/src/withdrawals/bus.ts new file mode 100644 index 00000000..4b2b5ffa --- /dev/null +++ b/packages/sdk/src/withdrawals/bus.ts @@ -0,0 +1,128 @@ +import { LidoSDKCore } from '../core/index.js'; + +import { LidoSDKWithdrawalsContract } from './withdrawalsContract.js'; +import { LidoSDKWithdrawalsViews } from './withdrawalsViews.js'; +import { LidoSDKWithdrawalsRequestsInfo } from './withdrawalsRequestsInfo.js'; +import { LidoSDKWithdrawalsPermit } from './withdrawalsPermit.js'; +import { LidoSDKWithdrawalsApprove } from './withdrawalsApprove.js'; +import { type LidoSDKWithdrawalsProps } from './types.js'; + +export class Bus { + props: LidoSDKWithdrawalsProps; + version: string | undefined; + + coreInstance: LidoSDKCore | undefined; + contractInstance: LidoSDKWithdrawalsContract | undefined; + viewsInstance: LidoSDKWithdrawalsViews | undefined; + requestsInfoInstance: LidoSDKWithdrawalsRequestsInfo | undefined; + permitInstance: LidoSDKWithdrawalsPermit | undefined; + approvalInstance: LidoSDKWithdrawalsApprove | undefined; + + constructor(props: LidoSDKWithdrawalsProps, version?: string) { + this.props = props; + this.version = version; + + if (props.core) this.core = props.core; + } + + // core + + get core(): LidoSDKCore { + if (!this.coreInstance) { + this.coreInstance = new LidoSDKCore(this.props, this.version); + return this.coreInstance; + } + return this.coreInstance; + } + + set core(core: LidoSDKCore) { + this.coreInstance = core; + } + + // Contract + + get contract(): LidoSDKWithdrawalsContract { + if (!this.contractInstance) { + this.contractInstance = new LidoSDKWithdrawalsContract({ + ...this.props, + bus: this, + }); + return this.contractInstance; + } + return this.contractInstance; + } + + set contract(contract: LidoSDKWithdrawalsContract) { + this.contractInstance = contract; + } + + // Views + + get views(): LidoSDKWithdrawalsViews { + if (!this.viewsInstance) { + this.viewsInstance = new LidoSDKWithdrawalsViews({ + ...this.props, + bus: this, + }); + return this.viewsInstance; + } + return this.viewsInstance; + } + + set views(views: LidoSDKWithdrawalsViews) { + this.viewsInstance = views; + } + + // Requests Info + + get requestsInfo(): LidoSDKWithdrawalsRequestsInfo { + if (!this.requestsInfoInstance) { + this.requestsInfoInstance = new LidoSDKWithdrawalsRequestsInfo({ + ...this.props, + bus: this, + }); + return this.requestsInfoInstance; + } + return this.requestsInfoInstance; + } + + set requestsInfo(requestsInfo: LidoSDKWithdrawalsRequestsInfo) { + this.requestsInfoInstance = requestsInfo; + } + + // Permit + + get permit(): LidoSDKWithdrawalsPermit { + if (!this.permitInstance) { + this.permitInstance = new LidoSDKWithdrawalsPermit({ + ...this.props, + bus: this, + }); + + return this.permitInstance; + } + + return this.permitInstance; + } + + set permit(permit: LidoSDKWithdrawalsPermit) { + this.permitInstance = permit; + } + + // Approval + + get approval(): LidoSDKWithdrawalsApprove { + if (!this.approvalInstance) { + this.approvalInstance = new LidoSDKWithdrawalsApprove({ + ...this.props, + bus: this, + }); + return this.approvalInstance; + } + return this.approvalInstance; + } + + set approval(approve: LidoSDKWithdrawalsApprove) { + this.approvalInstance = approve; + } +} diff --git a/packages/sdk/src/withdrawals/index.ts b/packages/sdk/src/withdrawals/index.ts index 62eadb9c..1e1de5e2 100644 --- a/packages/sdk/src/withdrawals/index.ts +++ b/packages/sdk/src/withdrawals/index.ts @@ -1,5 +1,9 @@ -export class LidoSDKWithdrawals { - constructor() { - console.log("LidoSDKWithdrawals"); - } -} +export { LidoSDKWithdrawals } from './withdrawals.js'; +export { + RequestCallbackStages, + RequestStageCallback, + RequestProps, + RequestWithPermitProps, + ApproveCallbackStages, + ApproveStageCallback, +} from './types.js'; diff --git a/packages/sdk/src/withdrawals/types.ts b/packages/sdk/src/withdrawals/types.ts new file mode 100644 index 00000000..e0ed9e43 --- /dev/null +++ b/packages/sdk/src/withdrawals/types.ts @@ -0,0 +1,123 @@ +import { type Address, type Hash, type TransactionReceipt } from 'viem'; + +import { type LidoSDKCoreProps, type LidoSDKCore } from '../core/index.js'; +import { type SDKError } from '../common/utils/index.js'; + +export type LidoSDKWithdrawalsProps = LidoSDKCoreProps & { + core?: LidoSDKCore; +}; + +export type RequestStatus = { + amountOfStETH: bigint; + amountOfShares: bigint; + owner: Address; + timestamp: bigint; + isFinalized: boolean; + isClaimed: boolean; +}; + +export type RequestStatusWithId = { + amountOfStETH: bigint; + amountOfShares: bigint; + owner: Address; + timestamp: bigint; + isFinalized: boolean; + isClaimed: boolean; + id: bigint; + stringId: string; +}; + +export type PermitWstETHStETHProps = { + amount: bigint; + account: Address; + spender: Address; + deadline: bigint; +}; + +export type PermitProps = PermitWstETHStETHProps & { + token: 'stETH' | 'wstETH'; +}; + +export type RequestWithPermitProps = { + account: Address; + amount: string; + requests: readonly bigint[]; + token: 'stETH' | 'wstETH'; + callback?: RequestStageCallback; +}; + +export type RequestProps = { + account: Address; + requests: readonly bigint[]; + token: 'stETH' | 'wstETH'; + callback?: RequestStageCallback; +}; + +export enum RequestCallbackStages { + 'BUNKER' = 'bunker', + 'PERMIT' = 'permit', + 'GAS_LIMIT' = 'gas_limit', + 'SIGN' = 'sign', + 'RECEIPT' = 'receipt', + 'CONFIRMATION' = 'confirmation', + 'DONE' = 'done', + 'MULTISIG_DONE' = 'multisig_done', + 'ERROR' = 'error', +} + +export type RequestCallbackProps = + | { stage: RequestCallbackStages.BUNKER; payload?: undefined } + | { stage: RequestCallbackStages.PERMIT; payload?: undefined } + | { stage: RequestCallbackStages.GAS_LIMIT; payload?: undefined } + | { stage: RequestCallbackStages.SIGN; payload?: bigint } + | { stage: RequestCallbackStages.RECEIPT; payload: Hash } + | { stage: RequestCallbackStages.CONFIRMATION; payload: TransactionReceipt } + | { stage: RequestCallbackStages.DONE; payload: bigint } + | { stage: RequestCallbackStages.MULTISIG_DONE; payload?: undefined } + | { stage: RequestCallbackStages.ERROR; payload: SDKError }; + +export type RequestStageCallback = (props: RequestCallbackProps) => void; + +export type Signature = { + v: number; + r: `0x${string}`; + s: `0x${string}`; + value: bigint; + deadline: bigint; + chainId: bigint | number; + nonce: `0x${string}`; + owner: Address; + spender: Address; +}; + +export enum ApproveCallbackStages { + 'GAS_LIMIT' = 'gas_limit', + 'SIGN' = 'sign', + 'RECEIPT' = 'receipt', + 'CONFIRMATION' = 'confirmation', + 'DONE' = 'done', + 'MULTISIG_DONE' = 'multisig_done', + 'ERROR' = 'error', +} + +export type ApproveCallbackProps = + | { stage: ApproveCallbackStages.GAS_LIMIT; payload?: undefined } + | { stage: ApproveCallbackStages.SIGN; payload?: bigint } + | { stage: ApproveCallbackStages.SIGN; payload: Hash } + | { stage: ApproveCallbackStages.RECEIPT; payload: Hash } + | { + stage: ApproveCallbackStages.CONFIRMATION; + payload: TransactionReceipt; + } + | { stage: ApproveCallbackStages.DONE; payload: bigint } + | { stage: ApproveCallbackStages.MULTISIG_DONE; payload?: undefined } + | { stage: ApproveCallbackStages.ERROR; payload: SDKError }; + +export type ApproveStageCallback = (props: ApproveCallbackProps) => void; + +export type ApproveProps = { + account: Address; + amount: string; + token: 'stETH' | 'wstETH'; + callback?: ApproveStageCallback; +}; diff --git a/packages/sdk/src/withdrawals/withdrawals.ts b/packages/sdk/src/withdrawals/withdrawals.ts new file mode 100644 index 00000000..3b6bc565 --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawals.ts @@ -0,0 +1,331 @@ +import { maxUint256, parseEther, type Address } from 'viem'; +import invariant from 'tiny-invariant'; + +import { Logger, Cache, ErrorHandler } from '../common/decorators/index.js'; +import { version } from '../version.js'; + +import { Bus } from './bus.js'; +import { + type LidoSDKWithdrawalsProps, + type RequestWithPermitProps, + type RequestProps, + type Signature, + RequestCallbackStages, +} from './types.js'; + +const INFINITY_DEADLINE_VALUE = maxUint256; + +export class LidoSDKWithdrawals extends Bus { + constructor(props: LidoSDKWithdrawalsProps) { + super(props, version); + } + + // Calls + + @Logger('Call:') + public async request(props: RequestWithPermitProps) { + const { account } = props; + invariant(this.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const isContract = await this.core.isContract(account); + + if (isContract) return this.requestMultisigByToken(props); + else return this.requestWithPermitByToken(props); + } + + @Logger('Call:') + public async requestWithoutPermit(props: RequestProps) { + const { account } = props; + invariant(this.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const isContract = await this.core.isContract(account); + + if (isContract) return this.requestMultisigByToken(props); + else return this.requestWithoutPermitByToken(props); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestStethWithPermit( + props: Omit, + ) { + this.requestWithPermitByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestWstethWithPermit( + props: Omit, + ) { + this.requestWithPermitByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestStethWithoutPermit(props: Omit) { + this.requestWithoutPermitByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestWstethWithoutPermit(props: Omit) { + this.requestWithoutPermitByToken({ ...props, token: 'wstETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestStethMultisig(props: Omit) { + return this.requestMultisigByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestWstethMultisig(props: Omit) { + return this.requestMultisigByToken({ ...props, token: 'wstETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestWithoutPermitByToken(props: RequestProps) { + const { account, requests, callback, token } = props; + invariant(this.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const contract = await this.contract.getContractWithdrawalsQueue(); + + const isSteth = token === 'stETH'; + let tokenRequestMethod; + + if (isSteth) { + tokenRequestMethod = contract.write.requestWithdrawals; + } else { + tokenRequestMethod = contract.write.requestWithdrawalsWstETH; + } + + const params = [requests, account] as const; + + callback?.({ stage: RequestCallbackStages.GAS_LIMIT }); + + const gasLimit = await this.requestGasLimitByToken( + account, + requests, + token, + ); + const feeData = await this.core.getFeeData(); + const overrides = { + account, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + }; + + callback?.({ stage: RequestCallbackStages.SIGN, payload: gasLimit }); + + const transaction = await tokenRequestMethod.call(this, [...params], { + ...overrides, + chain: this.core.chain, + }); + + callback?.({ stage: RequestCallbackStages.RECEIPT, payload: transaction }); + + const transactionReceipt = + await this.core.rpcProvider.waitForTransactionReceipt({ + hash: transaction, + }); + + callback?.({ + stage: RequestCallbackStages.CONFIRMATION, + payload: transactionReceipt, + }); + + const confirmations = + await this.core.rpcProvider.getTransactionConfirmations({ + hash: transactionReceipt.transactionHash, + }); + + callback?.({ stage: RequestCallbackStages.DONE, payload: confirmations }); + + return { hash: transaction, receipt: transactionReceipt, confirmations }; + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestWithPermitByToken(props: RequestWithPermitProps) { + const { account, amount, requests, callback, token } = props; + invariant(this.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const contract = await this.contract.getContractWithdrawalsQueue(); + + const isSteth = token === 'stETH'; + let tokenRequestMethod; + + if (isSteth) { + tokenRequestMethod = contract.write.requestWithdrawalsWithPermit; + } else { + tokenRequestMethod = contract.write.requestWithdrawalsWstETHWithPermit; + } + + callback?.({ stage: RequestCallbackStages.PERMIT }); + + const signature = await this.permit.permitSignature({ + account, + spender: contract.address, + deadline: INFINITY_DEADLINE_VALUE, + amount: parseEther(amount), + token, + }); + + const params = [ + requests, + signature.owner, + { + value: signature.value, + deadline: signature.deadline, + v: signature.v, + r: signature.r, + s: signature.s, + }, + ] as const; + + callback?.({ stage: RequestCallbackStages.GAS_LIMIT }); + + const gasLimit = await this.requestWithPermitGasLimitByToken( + account, + signature, + requests, + token, + ); + const feeData = await this.core.getFeeData(); + const overrides = { + account, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + }; + + callback?.({ stage: RequestCallbackStages.SIGN, payload: gasLimit }); + + const transaction = await tokenRequestMethod.call(this, [...params], { + chain: this.core.chain, + gas: gasLimit, + ...overrides, + }); + + callback?.({ stage: RequestCallbackStages.RECEIPT, payload: transaction }); + + const transactionReceipt = + await this.core.rpcProvider.waitForTransactionReceipt({ + hash: transaction, + }); + + callback?.({ + stage: RequestCallbackStages.CONFIRMATION, + payload: transactionReceipt, + }); + + const confirmations = + await this.core.rpcProvider.getTransactionConfirmations({ + hash: transactionReceipt.transactionHash, + }); + + callback?.({ stage: RequestCallbackStages.DONE, payload: confirmations }); + + return { hash: transaction, receipt: transactionReceipt, confirmations }; + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async requestMultisigByToken(props: RequestProps) { + const { account, requests, callback, token } = props; + invariant(this.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const contract = await this.contract.getContractWithdrawalsQueue(); + + const isSteth = token === 'stETH'; + let tokenRequestMethod; + + if (isSteth) tokenRequestMethod = contract.write.requestWithdrawals; + else tokenRequestMethod = contract.write.requestWithdrawalsWstETH; + + const params = [requests, account] as const; + + callback?.({ stage: RequestCallbackStages.SIGN }); + + const transaction = await tokenRequestMethod.call(this, [...params], { + account, + chain: this.core.chain, + }); + + callback?.({ stage: RequestCallbackStages.MULTISIG_DONE }); + + return { hash: transaction }; + } + + // Utils + + @Logger('Utils:') + @Cache(30 * 1000, ['core.chain.id']) + private async requestWithPermitGasLimitByToken( + account: Address, + signature: Signature, + requests: readonly bigint[], + token: 'stETH' | 'wstETH', + ): Promise { + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const contract = await this.contract.getContractWithdrawalsQueue(); + + const isSteth = token === 'stETH'; + let tokenRequestMethod; + + if (isSteth) + tokenRequestMethod = contract.estimateGas.requestWithdrawalsWithPermit; + else + tokenRequestMethod = + contract.estimateGas.requestWithdrawalsWstETHWithPermit; + + const params = [ + requests, + signature.owner, + { + value: signature.value, + deadline: signature.deadline, + v: signature.v, + r: signature.r, + s: signature.s, + }, + ] as const; + + const gasLimit = await tokenRequestMethod.call(this, [...params], { + account, + }); + + return gasLimit; + } + + @Logger('Utils:') + @Cache(30 * 1000, ['core.chain.id']) + private async requestGasLimitByToken( + account: Address, + requests: readonly bigint[], + token: 'stETH' | 'wstETH', + ): Promise { + invariant(this.core.rpcProvider, 'RPC provider is not defined'); + + const contract = await this.contract.getContractWithdrawalsQueue(); + + const isSteth = token === 'stETH'; + let tokenRequestMethod; + if (isSteth) tokenRequestMethod = contract.estimateGas.requestWithdrawals; + else tokenRequestMethod = contract.estimateGas.requestWithdrawalsWstETH; + + const params = [requests, account] as const; + const gasLimit = await tokenRequestMethod.call(this, [...params], { + account, + }); + + return gasLimit; + } +} diff --git a/packages/sdk/src/withdrawals/withdrawalsApprove.ts b/packages/sdk/src/withdrawals/withdrawalsApprove.ts new file mode 100644 index 00000000..46110dc2 --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawalsApprove.ts @@ -0,0 +1,260 @@ +import { parseEther, type Address } from 'viem'; +import invariant from 'tiny-invariant'; + +import { type LidoSDKCoreProps } from '../core/index.js'; +import { Logger, Cache, ErrorHandler } from '../common/decorators/index.js'; +import { version } from '../version.js'; + +import { Bus } from './bus.js'; +import { type ApproveProps, ApproveCallbackStages } from './types.js'; + +export class LidoSDKWithdrawalsApprove { + private readonly bus: Bus; + + constructor(props: LidoSDKCoreProps & { bus?: Bus }) { + if (props.bus) this.bus = props.bus; + else this.bus = new Bus(props, version); + } + + @Logger('Call:') + public async approve(props: ApproveProps) { + const { account } = props; + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.bus.core.rpcProvider, 'RPC provider is not defined'); + + const isContract = await this.bus.core.isContract(account); + + if (isContract) return await this.approveMultisigByToken(props); + else return await this.approveByToken(props); + } + + @Logger('Call:') + public async approveEOA(props: ApproveProps) { + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.bus.core.rpcProvider, 'RPC provider is not defined'); + + this.approveByToken(props); + } + + @Logger('Call:') + public async approveMultisig(props: ApproveProps) { + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + + return this.approveMultisigByToken(props); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async approveSteth(props: Omit) { + this.approveByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async approveWsteth(props: Omit) { + this.approveByToken({ ...props, token: 'wstETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + private async approveByToken(props: ApproveProps) { + const { account, amount, callback, token } = props; + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + invariant(this.bus.core.rpcProvider, 'RPC provider is not defined'); + + const isSteth = token === 'stETH'; + let tokenApproveMethod; + let gasLimitMethod; + + const addressWithdrawalsQueue = + await this.bus.contract.contractAddressWithdrawalsQueue(); + if (isSteth) { + tokenApproveMethod = (await this.bus.contract.getContractStETH()).write + .approve; + gasLimitMethod = this.approveStethGasLimit; + } else { + tokenApproveMethod = (await this.bus.contract.getContractWstETH()).write + .approve; + gasLimitMethod = this.approveWstethGasLimit; + } + + callback?.({ stage: ApproveCallbackStages.GAS_LIMIT }); + + const gasLimit = await gasLimitMethod.call(this, amount, account); + const feeData = await this.bus.core.getFeeData(); + const overrides = { + account, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + }; + + callback?.({ stage: ApproveCallbackStages.SIGN, payload: gasLimit }); + + const transaction = await tokenApproveMethod.call( + this, + [addressWithdrawalsQueue, parseEther(amount)], + { chain: this.bus.core.chain, ...overrides, gas: gasLimit }, + ); + + callback?.({ + stage: ApproveCallbackStages.RECEIPT, + payload: transaction, + }); + + const transactionReceipt = + await this.bus.core.rpcProvider.waitForTransactionReceipt({ + hash: transaction, + }); + + callback?.({ + stage: ApproveCallbackStages.CONFIRMATION, + payload: transactionReceipt, + }); + + const confirmations = + await this.bus.core.rpcProvider.getTransactionConfirmations({ + hash: transactionReceipt.transactionHash, + }); + + callback?.({ + stage: ApproveCallbackStages.DONE, + payload: confirmations, + }); + + return { hash: transaction, receipt: transactionReceipt, confirmations }; + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async approveStethMultisig(props: Omit) { + return this.approveMultisigByToken({ ...props, token: 'stETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + public async approveWstethMultisig(props: Omit) { + return this.approveMultisigByToken({ ...props, token: 'wstETH' }); + } + + @Logger('Call:') + @ErrorHandler('Error:') + private async approveMultisigByToken(props: ApproveProps) { + const { account, amount, callback, token } = props; + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + + const isSteth = token === 'stETH'; + let tokenApproveMethod; + + if (isSteth) + tokenApproveMethod = (await this.bus.contract.getContractStETH()).write + .approve; + else + tokenApproveMethod = (await this.bus.contract.getContractWstETH()).write + .approve; + + const addressWithdrawalsQueue = + await this.bus.contract.contractAddressWithdrawalsQueue(); + + callback?.({ stage: ApproveCallbackStages.SIGN }); + + const transaction = await tokenApproveMethod.call( + this, + [addressWithdrawalsQueue, parseEther(amount)], + { chain: this.bus.core.chain, account }, + ); + + callback?.({ + stage: ApproveCallbackStages.MULTISIG_DONE, + }); + + return { hash: transaction }; + } + + // Utils + + @Logger('Utils:') + @ErrorHandler('Error:') + public async approveStethGasLimit(amount: string, account: Address) { + return this.approveGasLimitByToken(amount, account, 'stETH'); + } + + @Logger('Utils:') + @ErrorHandler('Error:') + public async approveWstethGasLimit(amount: string, account: Address) { + return this.approveGasLimitByToken(amount, account, 'stETH'); + } + + @Logger('Utils:') + @Cache(30 * 1000, ['bus.core.chain.id']) + private async approveGasLimitByToken( + amount: string, + account: Address, + token: 'stETH' | 'wstETH', + ) { + invariant(this.bus.core.rpcProvider, 'RPC provider is not defined'); + + const isSteth = token === 'stETH'; + let estimateGasMethod; + + if (isSteth) + estimateGasMethod = (await this.bus.contract.getContractStETH()) + .estimateGas.approve; + else + estimateGasMethod = (await this.bus.contract.getContractWstETH()) + .estimateGas.approve; + + const addressWithdrawalsQueue = + await this.bus.contract.contractAddressWithdrawalsQueue(); + + const gasLimit = await estimateGasMethod.call( + this, + [addressWithdrawalsQueue, parseEther(amount)], + { account }, + ); + + return gasLimit; + } + + @Logger('Utils:') + @ErrorHandler('Error:') + public async checkApprovalSteth(amount: string, account: Address) { + return this.checkApprovalByToken(amount, account, 'wstETH'); + } + + @Logger('Utils:') + @ErrorHandler('Error:') + public async checkApprovalWsteth(amount: string, account: Address) { + return this.checkApprovalByToken(amount, account, 'wstETH'); + } + + @Logger('Utils:') + public async checkApprovalByToken( + amount: string, + account: Address, + token: 'stETH' | 'wstETH', + ) { + invariant(this.bus.core.rpcProvider, 'RPC provider is not defined'); + + const isSteth = token === 'stETH'; + let allowanceMethod; + + if (isSteth) + allowanceMethod = (await this.bus.contract.getContractStETH()).read + .allowance; + else + allowanceMethod = (await this.bus.contract.getContractWstETH()).read + .allowance; + + const addressWithdrawalsQueue = + await this.bus.contract.contractAddressWithdrawalsQueue(); + + const allowance = await allowanceMethod.call( + this, + [account, addressWithdrawalsQueue], + { account }, + ); + const isNeedApprove = allowance > parseEther(amount); + + return { allowance, isNeedApprove }; + } +} diff --git a/packages/sdk/src/withdrawals/withdrawalsContract.ts b/packages/sdk/src/withdrawals/withdrawalsContract.ts new file mode 100644 index 00000000..5e37c140 --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawalsContract.ts @@ -0,0 +1,107 @@ +import { getContract } from 'viem'; +import { + type Address, + type GetContractReturnType, + type PublicClient, + type WalletClient, +} from 'viem'; +import invariant from 'tiny-invariant'; + +import { Logger, Cache } from '../common/decorators/index.js'; +import { LIDO_CONTRACT_NAMES } from '../common/constants.js'; +import { version } from '../version.js'; +import { type LidoSDKCoreProps } from '../core/index.js'; + +import { WithdrawalsQueueAbi } from './abi/withdrawalsQueue.js'; +import { PartStethAbi } from './abi/partStETH.js'; +import { PartWstethAbi } from './abi/partWstETH.js'; +import { Bus } from './bus.js'; + +export class LidoSDKWithdrawalsContract { + private readonly bus: Bus; + + constructor(props: LidoSDKCoreProps & { bus?: Bus }) { + if (props.bus) this.bus = props.bus; + else this.bus = new Bus(props, version); + } + + // Contracts + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id']) + public async contractAddressWithdrawalsQueue(): Promise
{ + invariant(this.bus.core.chain, 'Chain is not defined'); + + return await this.bus.core.getContractAddress( + LIDO_CONTRACT_NAMES.withdrawalQueue, + ); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, [ + 'bus.core.chain.id', + 'contractAddressWithdrawalsQueue', + ]) + public async getContractWithdrawalsQueue(): Promise< + GetContractReturnType< + typeof WithdrawalsQueueAbi, + PublicClient, + WalletClient + > + > { + const address = await this.contractAddressWithdrawalsQueue(); + + return getContract({ + address, + abi: WithdrawalsQueueAbi, + publicClient: this.bus.core.rpcProvider, + walletClient: this.bus.core.web3Provider, + }); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id']) + public async contractAddressStETH(): Promise
{ + invariant(this.bus.core.chain, 'Chain is not defined'); + + return await this.bus.core.getContractAddress(LIDO_CONTRACT_NAMES.lido); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id', 'contractAddressStETH']) + public async getContractStETH(): Promise< + GetContractReturnType + > { + const address = await this.contractAddressStETH(); + + return getContract({ + address, + abi: PartStethAbi, + publicClient: this.bus.core.rpcProvider, + walletClient: this.bus.core.web3Provider, + }); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id']) + public async contractAddressWstETH(): Promise
{ + invariant(this.bus.core.chain, 'Chain is not defined'); + + return await this.bus.core.getContractAddress(LIDO_CONTRACT_NAMES.wsteth); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id', 'contractAddressWstETH']) + public async getContractWstETH(): Promise< + GetContractReturnType + > { + const address = await this.contractAddressWstETH(); + + return getContract({ + address, + abi: PartWstethAbi, + publicClient: this.bus.core.rpcProvider, + walletClient: this.bus.core.web3Provider, + }); + } +} diff --git a/packages/sdk/src/withdrawals/withdrawalsPermit.ts b/packages/sdk/src/withdrawals/withdrawalsPermit.ts new file mode 100644 index 00000000..c18e95fd --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawalsPermit.ts @@ -0,0 +1,155 @@ +import { numberToHex, isHex, stringify } from 'viem'; +import { splitSignature } from '@ethersproject/bytes'; +import invariant from 'tiny-invariant'; + +import { Logger } from '../common/decorators/index.js'; +import { version } from '../version.js'; +import { type LidoSDKCoreProps } from '../core/index.js'; + +import { Bus } from './bus.js'; +import { + type PermitWstETHStETHProps, + type PermitProps, + type Signature, +} from './types.js'; + +const EIP2612_TYPE = [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, +]; + +const TYPES = { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { + name: 'chainId', + type: 'uint256', + }, + { + name: 'verifyingContract', + type: 'address', + }, + ], + Permit: EIP2612_TYPE, +}; + +export class LidoSDKWithdrawalsPermit { + private readonly bus: Bus; + + constructor(props: LidoSDKCoreProps & { bus?: Bus }) { + if (props.bus) this.bus = props.bus; + else this.bus = new Bus(props, version); + } + + // Calls + + @Logger('Permit:') + public async permitSignature(props: PermitProps) { + const { token, ...rest } = props; + + if (token === 'stETH') return this.stethPermitSignature(rest); + return this.wstethPermitSignature(rest); + } + + @Logger('Permit:') + public async stethPermitSignature( + props: PermitWstETHStETHProps, + ): Promise { + const { amount, account, spender, deadline } = props; + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + + const contract = await this.bus.contract.getContractStETH(); + + const [name, version, chainId, verifyingContract] = + await contract.read.eip712Domain(); + const domain = { + name, + version, + chainId: Number(chainId), + verifyingContract, + }; + const nonce = await contract.read.nonces([account]); + + const message = { + owner: account, + spender, + value: amount.toString(), + nonce: numberToHex(nonce), + deadline: numberToHex(deadline), + }; + const typedData = stringify( + { domain, primaryType: 'Permit', types: TYPES, message }, + (_, value) => (isHex(value) ? value.toLowerCase() : value), + ); + + const signature = await this.bus.core.web3Provider.request({ + method: 'eth_signTypedData_v4', + params: [account, typedData], + }); + const { s, r, v } = splitSignature(signature); + + return { + v, + r: r as `0x${string}`, + s: s as `0x${string}`, + value: amount, + deadline, + chainId: chainId, + nonce: message.nonce, + owner: account, + spender, + }; + } + + @Logger('Permit:') + public async wstethPermitSignature( + props: PermitWstETHStETHProps, + ): Promise { + const { amount, account, spender, deadline } = props; + invariant(this.bus.core.web3Provider, 'Web3 provider is not defined'); + + const contract = await this.bus.contract.getContractWstETH(); + + const domain = { + name: 'Wrapped liquid staked Ether 2.0', + version: '1', + chainId: this.bus.core.chain.id, + verifyingContract: contract.address, + }; + const nonce = await contract.read.nonces([account]); + + const message = { + owner: account, + spender, + value: amount.toString(), + nonce: numberToHex(nonce), + deadline: numberToHex(deadline), + }; + const typedData = stringify( + { domain, primaryType: 'Permit', TYPES, message }, + (_, value) => (isHex(value) ? value.toLowerCase() : value), + ); + + const signature = await this.bus.core.web3Provider.request({ + method: 'eth_signTypedData_v4', + params: [account, typedData], + }); + const { s, r, v } = splitSignature(signature); + + return { + v, + r: r as `0x${string}`, + s: s as `0x${string}`, + value: amount, + deadline, + chainId: this.bus.core.chain.id, + nonce: message.nonce, + owner: account, + spender, + }; + } +} diff --git a/packages/sdk/src/withdrawals/withdrawalsRequestsInfo.ts b/packages/sdk/src/withdrawals/withdrawalsRequestsInfo.ts new file mode 100644 index 00000000..bd29c5c7 --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawalsRequestsInfo.ts @@ -0,0 +1,123 @@ +import { type Address } from 'viem'; + +import { Logger } from '../common/decorators/index.js'; +import { isBigint } from '../common/utils/index.js'; +import { version } from '../version.js'; +import { type LidoSDKCoreProps } from '../core/index.js'; + +import { Bus } from './bus.js'; +import { type RequestStatusWithId } from './types.js'; + +export class LidoSDKWithdrawalsRequestsInfo { + private readonly bus: Bus; + + constructor(props: LidoSDKCoreProps & { bus?: Bus }) { + if (props.bus) this.bus = props.bus; + else this.bus = new Bus(props, version); + } + + // Utils + + @Logger('Utils:') + public async getWithdrawalRequestsInfo(props: { account: Address }) { + const { account } = props; + + const claimableInfo = await this.getClaimableRequestsInfo({ account }); + const claimableETH = await this.getClaimableRequestsETH({ + claimableRequestsIds: claimableInfo.claimableRequests, + }); + const pendingInfo = await this.getPendingRequestsInfo({ account }); + + return { + claimableInfo, + pendingInfo, + claimableETH, + }; + } + + @Logger('Utils:') + public async getWithdrawalRequestsStatus(props: { + account: Address; + }): Promise { + const requestsIds = await this.bus.views.getWithdrawalRequestsIds({ + account: props.account, + }); + + return this.bus.views.getWithdrawalStatus({ requestsIds }); + } + + @Logger('Utils:') + public async getClaimableRequestsInfo(props: { account: Address }): Promise<{ + claimableRequests: RequestStatusWithId[]; + claimableAmountStETH: bigint; + }> { + const requests = await this.getWithdrawalRequestsStatus(props); + + return requests.reduce( + (acc, request) => { + if (request.isFinalized && !request.isClaimed) { + acc.claimableRequests.push(request); + acc.claimableAmountStETH += request.amountOfStETH; + } + return acc; + }, + { claimableRequests: [], claimableAmountStETH: BigInt(0) } as { + claimableRequests: RequestStatusWithId[]; + claimableAmountStETH: bigint; + }, + ); + } + + @Logger('Utils:') + public async getClaimableRequestsETH(props: { + claimableRequestsIds: (bigint | RequestStatusWithId)[]; + }): Promise<{ ethByRequests: readonly bigint[]; ethSum: bigint }> { + const sortedIds = props.claimableRequestsIds + .sort((aReq, bReq) => { + if (isBigint(aReq) && isBigint(bReq)) { + return aReq > bReq ? 1 : -1; + } + if (!isBigint(aReq) && !isBigint(bReq)) { + return aReq.id > bReq.id ? 1 : -1; + } + + return 0; + }) + .map((req) => (isBigint(req) ? req : req.id)); + + const hints = await this.bus.views.findCheckpointHints({ + sortedIds, + lastIndex: await this.bus.views.getLastCheckpointIndex(), + }); + + const ethByRequests = await this.bus.views.getClaimableEther({ + sortedIds, + hints, + }); + const ethSum = ethByRequests.reduce((acc, eth) => acc + eth, BigInt(0)); + + return { ethByRequests, ethSum }; + } + + @Logger('Utils:') + public async getPendingRequestsInfo(props: { account: Address }): Promise<{ + pendingRequests: RequestStatusWithId[]; + pendingAmountStETH: bigint; + }> { + const requests = await this.getWithdrawalRequestsStatus(props); + + return requests.reduce( + (acc, request) => { + if (!request.isFinalized && !request.isClaimed) { + acc.pendingRequests.push(request); + acc.pendingAmountStETH += request.amountOfStETH; + } + return acc; + }, + { pendingRequests: [], pendingAmountStETH: BigInt(0) } as { + pendingRequests: RequestStatusWithId[]; + pendingAmountStETH: bigint; + }, + ); + } +} diff --git a/packages/sdk/src/withdrawals/withdrawalsViews.ts b/packages/sdk/src/withdrawals/withdrawalsViews.ts new file mode 100644 index 00000000..10634aaf --- /dev/null +++ b/packages/sdk/src/withdrawals/withdrawalsViews.ts @@ -0,0 +1,126 @@ +import { type Address } from 'viem'; +import invariant from 'tiny-invariant'; + +import { Logger, Cache } from '../common/decorators/index.js'; +import { version } from '../version.js'; +import { type LidoSDKCoreProps } from '../core/index.js'; + +import { Bus } from './bus.js'; +import { type RequestStatusWithId } from './types.js'; + +export class LidoSDKWithdrawalsViews { + private readonly bus: Bus; + + constructor(props: LidoSDKCoreProps & { bus?: Bus }) { + if (props.bus) this.bus = props.bus; + else this.bus = new Bus(props, version); + } + + // Views + @Logger('Views:') + public async getWithdrawalRequestsIds(props: { + account: Address; + }): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.getWithdrawalRequests([props.account]); + } + + @Logger('Views:') + public async getLastCheckpointIndex(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.getLastCheckpointIndex(); + } + + @Logger('Views:') + public async getWithdrawalStatus(props: { + requestsIds: readonly bigint[]; + }): Promise { + const { requestsIds } = props; + + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + const requests = await contract.read.getWithdrawalStatus([requestsIds]); + + invariant(requests.length === requestsIds.length, 'Invalid requests ids'); + + return requests.map((request, i) => ({ + ...request, + id: requestsIds[i] || BigInt(0), + stringId: requestsIds[i]?.toString() || '0', + })); + } + + @Logger('Views:') + public async findCheckpointHints(props: { + sortedIds: bigint[]; + firstIndex?: bigint; + lastIndex: bigint; + }): Promise { + const { sortedIds, firstIndex = BigInt(1), lastIndex } = props; + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.findCheckpointHints([ + sortedIds, + firstIndex, + lastIndex, + ]); + } + + @Logger('Views:') + public async getClaimableEther(props: { + sortedIds: bigint[]; + hints: readonly bigint[]; + }): Promise { + const { sortedIds, hints } = props; + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.getClaimableEther([sortedIds, hints]); + } + + @Logger('Views:') + public async getUnfinalizedStETH(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.unfinalizedStETH(); + } + + // Constants + + @Logger('Views:') + public async minStethWithdrawalAmount(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.MIN_STETH_WITHDRAWAL_AMOUNT(); + } + + @Logger('Views:') + @Cache(30 * 60 * 1000, ['bus.core.chain.id']) + public async maxStethWithdrawalAmount(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.MAX_STETH_WITHDRAWAL_AMOUNT(); + } + + @Logger('Views:') + public async isPaused(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.isPaused(); + } + + @Logger('Views:') + public async isBunkerModeActive(): Promise { + const contract = await this.bus.contract.getContractWithdrawalsQueue(); + + return contract.read.isBunkerModeActive(); + } + + @Logger('Views:') + public async isTurboModeActive(): Promise { + const isBunkerMode = await this.isBunkerModeActive(); + const isPaused = await this.isPaused(); + + return !isPaused && !isBunkerMode; + } +} diff --git a/yarn.lock b/yarn.lock index ab5d6cc4..cd1252bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2726,6 +2726,7 @@ __metadata: version: 0.0.0-use.local resolution: "@lidofinance/lido-ethereum-sdk@workspace:packages/sdk" dependencies: + "@ethersproject/bytes": ^5.7.0 "@types/fs-extra": ^11.0.1 fs-extra: ^11.1.1 rimraf: ^5.0.1