Skip to content

Commit

Permalink
Merge pull request #117 from lidofinance/feature/si-1215-add-wwt
Browse files Browse the repository at this point in the history
[Feature] SI-1183 add-to-sdk-as-method-by-api
  • Loading branch information
Jeday committed Mar 26, 2024
2 parents 74277fe + 1a6bed0 commit 5506c95
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 3 deletions.
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
3 changes: 3 additions & 0 deletions packages/sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
### Added

- `Sepolia` testnet
- New method `getWithdrawalWaitingTimeByAmount` for fetching withdrawal waiting time for amount of eth
- New method `getWithdrawalWaitingTimeByRequestIds` for fetching withdrawal waiting time for earlier created requests by their ids

## Playground

- Support for `Sepolia` testnet
- Added blocks with new methods `getWithdrawalWaitingTimeByAmount` and `getWithdrawalWaitingTimeByRequestIds`

# 3.1.0

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
7 changes: 7 additions & 0 deletions packages/sdk/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,10 @@ export const VIEM_CHAINS: { [key in CHAINS]: Chain } = {
[CHAINS.Holesky]: holesky,
[CHAINS.Sepolia]: sepolia,
};

export const WQ_API_URLS: { [key in CHAINS]: string | null } = {
[CHAINS.Mainnet]: 'https://wq-api.lido.fi',
[CHAINS.Goerli]: 'https://wq-api.testnet.fi',
[CHAINS.Holesky]: 'https://wq-api-holesky.testnet.fi',
[CHAINS.Sepolia]: null,
};
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:';
29 changes: 29 additions & 0 deletions packages/sdk/src/withdraw/__test__/withdraw-waiting-time.test.ts
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
@@ -1,6 +1,7 @@
import type { Address } from 'viem';
import type { Bus } from './bus.js';
import type { AccountValue } from '../index.js';
import { CHAINS } from '../index.js';

export type LidoSDKWithdrawModuleProps = { bus: Bus; version?: string };

Expand Down Expand Up @@ -51,3 +52,62 @@ export type GetWithdrawalRequestsInfoReturnType = {
pendingInfo: GetPendingRequestsInfoReturnType;
claimableETH: GetClaimableRequestsETHByAccountReturnType;
};

export type WqApiCustomUrlGetter = (
defaultUrl: string | null,
chainId: CHAINS,
) => string;

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

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

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

export type WithdrawalWaitingTimeByRequestIdsParams = {
ids: readonly bigint[];
requestDelay?: number;
getCustomApiUrl?: WqApiCustomUrlGetter;
};

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',
}
112 changes: 112 additions & 0 deletions packages/sdk/src/withdraw/withdraw-waiting-time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Logger, ErrorHandler } from '../common/decorators/index.js';

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

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,
): Promise<WithdrawalWaitingTimeByAmountResponse> {
const getCustomApiUrl = props?.getCustomApiUrl;

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

const baseUrl = this.getBaseUrl(getCustomApiUrl);
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,
): Promise<readonly WithdrawalWaitingTimeRequestInfo[]> {
const requestDelay = props?.requestDelay ?? 1000;
const getCustomApiUrl = props?.getCustomApiUrl;

if (!Array.isArray(props.ids) || props.ids.length === 0) {
throw this.bus.core.error({
code: ERROR_CODE.INVALID_ARGUMENT,
message: 'expected not empty array ids',
});
}

const idsPages = [];
const pageSize = 20;
const baseUrl = this.getBaseUrl(getCustomApiUrl);
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;
}

getBaseUrl(getCustomApiUrl?: WqApiCustomUrlGetter) {
const defaultUrl = WQ_API_URLS[this.bus.core.chainId];

const baseUrl =
getCustomApiUrl && typeof getCustomApiUrl === 'function'
? getCustomApiUrl(defaultUrl, this.bus.core.chainId)
: defaultUrl;

if (!baseUrl) {
throw this.bus.core.error({
code: ERROR_CODE.INVALID_ARGUMENT,
message: `wq-api URL is not found for chain ${this.bus.core.chainId}, use getCustomApiUrl prop to setup custom URL`,
});
}

return baseUrl;
}
}
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>
);
};

0 comments on commit 5506c95

Please sign in to comment.