Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] SI-1183 add-to-sdk-as-method-by-api #117

Merged
merged 13 commits into from
Mar 26, 2024
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ console.log(balanceETH.toString(), 'ETH balance');

## Migration

For breaking changes between versions see [MIGRATION.md](MIGRATION.md)
For breaking changes between versions see [MIGRATION.md](packages/sdk/MIGRATION.md)

## Documentation

Expand Down
52 changes: 51 additions & 1 deletion packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ For breaking changes between versions see [MIGRATION.md](MIGRATION.md)
- [Views](#views)
- [Constants](#constants)
- [Requests info](#requests-info)
- [Waiting time](#waiting-time)
- [Get time by amount](#get-time-by-amount)
- [Get time by request ids](#get-time-by-request-ids)
- [(w)stETH](#wsteth)
- [unstETH NFT](#unsteth-nft)
- [Shares](#shares)
Expand Down Expand Up @@ -654,7 +657,7 @@ try {
console.log(
'transaction hash, transaction receipt, confirmations',
requestResult,
'array of requests(nfts) created with ids, amounts,creator, owner'
'array of requests(nfts) created with ids, amounts,creator, owner',
request.results.requests,
);
} catch (error) {
Expand Down Expand Up @@ -1010,6 +1013,53 @@ try {
- `pendingRequests` (Type: Array[RequestStatusWithId]): A list of requests pending finalization.
- `pendingAmountStETH` (Type: bigint): The amount of ETH pending claiming.

### Waiting time

#### Methods

##### Get time by amount

###### `getWithdrawalWaitingTimeByAmount`

###### Input Parameters:

- `props: { amount?: bigint }`
- `amount?` (Type: bigint **optional**): The amount of withdrawable eth. In case when it is not passed, it is calculated as default information about queue.

##### Output Parameters:

- Type: Object
- Structure:
- `requestInfo` (Type: Object): Information about withdrawal request
- `finalizationIn` (Type: number): The time needed for withdrawal in milliseconds.
- `finalizationAt` (Type: string): The time when request finalized for withdrawal.
- `type` (Type: WaitingTimeCalculationType): Type of final source of eth for withdrawal.
- `status` (Type: WaitingTimeStatus): Status of withdrawal request.
- `nextCalculationAt` (Type: string): Time when next calculation can be changed.

##### Get time by request ids

###### `getWithdrawalWaitingTimeByRequestIds`

###### Input Parameters:

- `props: { ids: bigint[] }`
- `ids` (ids: Array[bigint]): The ids of withdrawal requests.

##### Output Parameters:

- Type: Array of WithdrawalWaitingTimeRequestInfo objects
- Structure of each object:
- `requestInfo` (Type: RequestByIdInfoDto): Information about withdrawal request.
- `finalizationIn` (Type: number): The time needed for withdrawal in milliseconds.
- `finalizationAt` (Type: string): The time when request finalized for withdrawal.
- `requestId` (Type: string): The request id.
- `requestedAt` (Type: string): The time when withdrawal requested.
- `type` (Type: WaitingTimeCalculationType): Type of final source of eth for withdrawal.
- `status` (Type: WaitingTimeStatus): Status of withdrawal request.
- `nextCalculationAt` (Type: string): Time when next calculation can be changed.


## (w)stETH

stETH and wstETH tokens functionality is presented trough modules with same ERC20 interface that exposes balances, allowances, transfers and ERC2612 permits signing.
Expand Down
6 changes: 6 additions & 0 deletions packages/sdk/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,9 @@ export const VIEM_CHAINS: { [key in CHAINS]: Chain } = {
[CHAINS.Goerli]: goerli,
[CHAINS.Holesky]: holesky,
};

export const WQ_API_URLS: { [key in CHAINS]: string } = {
[CHAINS.Mainnet]: 'https://wq-api.lido.fi',
[CHAINS.Goerli]: 'https://wq-api.testnet.fi',
[CHAINS.Holesky]: 'https://wq-api-holesky.testnet.fi',
};
1 change: 1 addition & 0 deletions packages/sdk/src/common/decorators/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const ConsoleCss: Record<HeadMessage, string> = {
'Events:': 'color: salmon',
'Statistic:': 'color: purple',
'Rewards:': 'color: greenyellow',
'API:': 'color: mediumpurple',
'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;',
};
3 changes: 2 additions & 1 deletion packages/sdk/src/common/decorators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export type HeadMessage =
| 'Events:'
| 'Statistic:'
| 'Rewards:'
| 'Deprecation:';
| 'Deprecation:'
| 'API:';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect, describe, test } from '@jest/globals';
import { useWithdraw } from '../../../tests/utils/fixtures/use-withdraw.js';
import { WithdrawalWaitingTimeByRequestIdsParams } from '../types.js';

describe('withdraw waiting time', () => {
const withdraw = useWithdraw();
const { waitingTime } = withdraw;

test('can get withdrawal waiting time by amount', async () => {
const amount = 110n;
const requestInfos = await waitingTime.getWithdrawalWaitingTimeByAmount({
amount,
});
expect(typeof requestInfos.status).toEqual('string');
});

test('can get withdrawal waiting time by request ids', async () => {
const requestsIds = [1234n, 1235n];

const requestInfos = await waitingTime.getWithdrawalWaitingTimeByRequestIds(
{
ids: requestsIds,
} as WithdrawalWaitingTimeByRequestIdsParams,
);

expect(Array.isArray(requestInfos)).toEqual(true);
expect(requestInfos.length).toEqual(requestsIds.length);
});
});
14 changes: 14 additions & 0 deletions packages/sdk/src/withdraw/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LidoSDKWithdrawApprove,
} from './request/index.js';
import { LidoSDKModule } from '../common/class-primitives/sdk-module.js';
import { LidoSDKWithdrawWaitingTime } from './withdraw-waiting-time.js';

export class Bus extends LidoSDKModule {
private version: string | undefined;
Expand All @@ -17,6 +18,7 @@ export class Bus extends LidoSDKModule {
private approvalInstance: LidoSDKWithdrawApprove | undefined;
private claimInstance: LidoSDKWithdrawClaim | undefined;
private requestInstance: LidoSDKWithdrawRequest | undefined;
private waitingTimeInstance: LidoSDKWithdrawWaitingTime | undefined;

// Contract

Expand Down Expand Up @@ -89,4 +91,16 @@ export class Bus extends LidoSDKModule {
}
return this.requestInstance;
}

// Waiting Time

get waitingTime(): LidoSDKWithdrawWaitingTime {
if (!this.waitingTimeInstance) {
this.waitingTimeInstance = new LidoSDKWithdrawWaitingTime({
bus: this,
version: this.version,
});
}
return this.waitingTimeInstance;
}
}
60 changes: 60 additions & 0 deletions packages/sdk/src/withdraw/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,63 @@ export type GetWithdrawalRequestsInfoReturnType = {
pendingInfo: GetPendingRequestsInfoReturnType;
claimableETH: GetClaimableRequestsETHByAccountReturnType;
};

export type WithdrawalWaitingTimeByAmountParams = {
amount?: bigint;
};

export type RequestInfo = {
finalizationIn: number;
finalizationAt: string;
type: WaitingTimeCalculationType;
};

export type WithdrawalWaitingTimeByAmountResponse = {
requestInfo: RequestInfo;
status: WaitingTimeStatus;
nextCalculationAt: string;
};

export type WithdrawalWaitingTimeByAmountOptions = {
getWqApiURL?: () => string;
};

export type WithdrawalWaitingTimeByRequestIdsParams = {
ids: readonly bigint[];
};

export type WithdrawalWaitingTimeByRequestIdsOptions = {
requestDelay?: number;
getWqApiURL?: () => string;
};

export type RequestByIdInfo = {
finalizationIn: number;
finalizationAt: string;
requestId?: string;
requestedAt?: string;
type: WaitingTimeCalculationType;
};

export type WithdrawalWaitingTimeRequestInfo = {
requestInfo: RequestByIdInfo;
status: WaitingTimeStatus;
nextCalculationAt: string;
};

export enum WaitingTimeStatus {
initializing = 'initializing',
calculating = 'calculating',
finalized = 'finalized',
calculated = 'calculated',
}

export enum WaitingTimeCalculationType {
buffer = 'buffer',
bunker = 'bunker',
vaultsBalance = 'vaultsBalance',
rewardsOnly = 'rewardsOnly',
validatorBalances = 'validatorBalances',
requestTimestampMargin = 'requestTimestampMargin',
exitValidators = 'exitValidators',
}
104 changes: 104 additions & 0 deletions packages/sdk/src/withdraw/withdraw-waiting-time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Logger, ErrorHandler } from '../common/decorators/index.js';

import { BusModule } from './bus-module.js';
import type {
WithdrawalWaitingTimeByAmountResponse,
WithdrawalWaitingTimeRequestInfo,
WithdrawalWaitingTimeByAmountParams,
WithdrawalWaitingTimeByRequestIdsParams,
} from './types.js';
import { WQ_API_URLS } from '../common/index.js';
import { formatEther } from 'viem';
import {
WithdrawalWaitingTimeByAmountOptions,
WithdrawalWaitingTimeByRequestIdsOptions,
} from './types.js';

const endpoints = {
calculateByAmount: '/v2/request-time/calculate',
calculateByRequestId: '/v2/request-time',
};

export class LidoSDKWithdrawWaitingTime extends BusModule {
// API call integrations
@Logger('API:')
@ErrorHandler()
public async getWithdrawalWaitingTimeByAmount(
props: WithdrawalWaitingTimeByAmountParams,
options?: WithdrawalWaitingTimeByAmountOptions,
): Promise<WithdrawalWaitingTimeByAmountResponse> {
const getWqApiURL = options?.getWqApiURL;

const query = new URLSearchParams();
if (props.amount) {
query.set('amount', formatEther(props.amount));
}

const baseUrl =
getWqApiURL && typeof getWqApiURL === 'function'
? getWqApiURL()
: WQ_API_URLS[this.bus.core.chainId];

const url = `${baseUrl}${endpoints.calculateByAmount}?${query.toString()}`;

const response = await fetch(url, {
headers: {
'WQ-Request-Source': 'sdk',
},
});

return response.json();
}

@Logger('API:')
@ErrorHandler()
public async getWithdrawalWaitingTimeByRequestIds(
props: WithdrawalWaitingTimeByRequestIdsParams,
options?: WithdrawalWaitingTimeByRequestIdsOptions,
): Promise<readonly WithdrawalWaitingTimeRequestInfo[]> {
const requestDelay = options?.requestDelay ?? 1000;
const getWqApiURL = options?.getWqApiURL;

if (!Array.isArray(props.ids) || props.ids.length === 0) {
throw new Error('expected not empty array ids');
}

const idsPages = [];
const pageSize = 20;
const baseUrl =
getWqApiURL && typeof getWqApiURL === 'function'
? getWqApiURL()
: WQ_API_URLS[this.bus.core.chainId];

const path = `${baseUrl}${endpoints.calculateByRequestId}`;

for (let i = 0; i < props.ids.length; i += pageSize) {
idsPages.push(props.ids.slice(i, i + pageSize));
}

const result = [];

for (const page of idsPages) {
const query = new URLSearchParams();
query.set('ids', page.toString());

const url = `${path}?${query.toString()}`;

const response = await fetch(url, {
headers: {
'WQ-Request-Source': 'sdk',
},
});

const requests = await response.json();
result.push(...requests);

if (idsPages.length > 1) {
// avoid backend spam
await new Promise((resolve) => setTimeout(resolve, requestDelay));
}
}

return result;
}
}
23 changes: 23 additions & 0 deletions playground/demo/withdrawals/request.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,29 @@ export const WithdrawalsRequestDemo = () => {
})
}
/>

<Action
title={`Withdrawal Waiting Time By Amount`}
walletAction
action={() =>
withdraw.waitingTime.getWithdrawalWaitingTimeByAmount({
amount,
})
}
/>

<Action
title={`Withdrawal Waiting Time For Account Requests`}
walletAction
action={async () => {
const ids = await withdraw.views.getWithdrawalRequestsIds({
account,
});
return withdraw.waitingTime.getWithdrawalWaitingTimeByRequestIds({
ids,
});
}}
/>
</Accordion>
);
};
Loading