From aa3853809376c69c78a43167fd6632b7a5ed5cc6 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 12 Sep 2024 17:40:01 +0700 Subject: [PATCH 01/28] feat: L2 chains constants --- packages/sdk/src/common/constants.ts | 32 +++++++++++++++-- packages/sdk/src/core/abi/wq.ts | 2 +- packages/sdk/src/core/core.ts | 51 +++++++++++++++++++--------- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/packages/sdk/src/common/constants.ts b/packages/sdk/src/common/constants.ts index b81e29a9..d96e60f6 100644 --- a/packages/sdk/src/common/constants.ts +++ b/packages/sdk/src/common/constants.ts @@ -1,11 +1,18 @@ import { type Address, type Chain } from 'viem'; -import { goerli, mainnet, holesky, sepolia } from 'viem/chains'; +import { + goerli, + mainnet, + holesky, + sepolia, + optimismSepolia, +} from 'viem/chains'; export enum CHAINS { Goerli = 5, Mainnet = 1, Holesky = 17000, Sepolia = 11155111, + OptimismSepolia = 11155420, } export const APPROX_BLOCKS_BY_DAY = 7600n; @@ -15,6 +22,7 @@ export const SUPPORTED_CHAINS: CHAINS[] = [ CHAINS.Mainnet, CHAINS.Holesky, CHAINS.Sepolia, + CHAINS.OptimismSepolia, ]; export const SUBMIT_EXTRA_GAS_TRANSACTION_RATIO = 1.05; @@ -22,12 +30,13 @@ export const GAS_TRANSACTION_RATIO_PRECISION = 10 ** 7; export const ESTIMATE_ACCOUNT = '0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1'; export const LIDO_LOCATOR_BY_CHAIN: { - [key in CHAINS]: Address; + [key in CHAINS]: Address | null; } = { [CHAINS.Mainnet]: '0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb', [CHAINS.Goerli]: '0x1eDf09b5023DC86737b59dE68a8130De878984f5', [CHAINS.Holesky]: '0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8', [CHAINS.Sepolia]: '0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7', + [CHAINS.OptimismSepolia]: null, }; export const SUBRGRAPH_ID_BY_CHAIN: { @@ -37,6 +46,7 @@ export const SUBRGRAPH_ID_BY_CHAIN: { [CHAINS.Goerli]: 'QmeDfGTuNbSoZ71zi3Ch4WNRbzALfiFPnJMYUFPinLiFNa', [CHAINS.Holesky]: null, [CHAINS.Sepolia]: null, + [CHAINS.OptimismSepolia]: null, }; export const EARLIEST_TOKEN_REBASED_EVENT: { @@ -46,6 +56,7 @@ export const EARLIEST_TOKEN_REBASED_EVENT: { [CHAINS.Goerli]: 8712039n, [CHAINS.Holesky]: 52174n, [CHAINS.Sepolia]: 5434668n, + [CHAINS.OptimismSepolia]: 0n, } as const; export const LIDO_TOKENS = { @@ -73,6 +84,21 @@ export const enum LIDO_CONTRACT_NAMES { wsteth = 'wsteth', } +export const enum LIDO_L2_CONTRACT_NAMES { + wsteth = 'wsteth', + steth = 'steth', +} + +export const LIDO_L2_CONTRACT_ADDRESSES: Record< + number, + Record +> = { + [CHAINS.OptimismSepolia]: { + wsteth: '0x24B47cd3A74f1799b32B2de11073764Cb1bb318B', + steth: '0xf49d208b5c7b10415c7beafe9e656f2df9edfe3b', + }, +} as const; + export const CONTRACTS_BY_TOKENS = { [LIDO_TOKENS.steth]: LIDO_CONTRACT_NAMES.lido, [LIDO_TOKENS.wsteth]: LIDO_CONTRACT_NAMES.wsteth, @@ -110,6 +136,7 @@ export const VIEM_CHAINS: { [key in CHAINS]: Chain } = { [CHAINS.Goerli]: goerli, [CHAINS.Holesky]: holesky, [CHAINS.Sepolia]: sepolia, + [CHAINS.OptimismSepolia]: optimismSepolia, }; export const WQ_API_URLS: { [key in CHAINS]: string | null } = { @@ -117,4 +144,5 @@ export const WQ_API_URLS: { [key in CHAINS]: string | null } = { [CHAINS.Goerli]: 'https://wq-api.testnet.fi', [CHAINS.Holesky]: 'https://wq-api-holesky.testnet.fi', [CHAINS.Sepolia]: null, + [CHAINS.OptimismSepolia]: null, }; diff --git a/packages/sdk/src/core/abi/wq.ts b/packages/sdk/src/core/abi/wq.ts index d24e641b..ad0503f0 100644 --- a/packages/sdk/src/core/abi/wq.ts +++ b/packages/sdk/src/core/abi/wq.ts @@ -1,4 +1,4 @@ -export const wqAbi = [ +export const wqWstethAddressAbi = [ { inputs: [], name: 'WSTETH', diff --git a/packages/sdk/src/core/core.ts b/packages/sdk/src/core/core.ts index c471b65c..518a3acb 100644 --- a/packages/sdk/src/core/core.ts +++ b/packages/sdk/src/core/core.ts @@ -37,10 +37,12 @@ import { SUBRGRAPH_ID_BY_CHAIN, APPROX_SECONDS_PER_BLOCK, NOOP, + LIDO_L2_CONTRACT_NAMES, + LIDO_L2_CONTRACT_ADDRESSES, } from '../common/constants.js'; import { LidoLocatorAbi } from './abi/lidoLocator.js'; -import { wqAbi } from './abi/wq.js'; +import { wqWstethAddressAbi } from './abi/wq.js'; import type { LidoSDKCoreProps, PermitSignature, @@ -57,6 +59,7 @@ import type { import { TransactionCallbackStage } from './types.js'; import { permitAbi } from './abi/permit.js'; import { LidoSDKCacheable } from '../common/class-primitives/cacheable.js'; +import { readContract } from 'viem/actions'; export default class LidoSDKCore extends LidoSDKCacheable { public static readonly INFINITY_DEADLINE_VALUE = maxUint256; @@ -170,7 +173,13 @@ export default class LidoSDKCore extends LidoSDKCacheable { @Logger('Contracts:') @Cache(30 * 60 * 1000, ['chain.id']) public contractAddressLidoLocator(): Address { - return LIDO_LOCATOR_BY_CHAIN[this.chain.id as CHAINS]; + const locator = LIDO_LOCATOR_BY_CHAIN[this.chain.id as CHAINS]; + invariant( + locator, + `Lido Ethereum Contacts are not supported on ${this.chain.name}(${this.chain.id})`, + ERROR_CODE.NOT_SUPPORTED, + ); + return locator; } @Logger('Contracts:') @@ -186,18 +195,6 @@ export default class LidoSDKCore extends LidoSDKCacheable { }); } - @Logger('Contracts:') - @Cache(30 * 60 * 1000, ['chain.id']) - private getContractWQ( - address: Address, - ): GetContractReturnType { - return getContract({ - address, - abi: wqAbi, - client: this.rpcProvider, - }); - } - // PERMIT @Logger('Permit:') public async signPermit(props: SignPermitProps): Promise { @@ -377,8 +374,12 @@ export default class LidoSDKCore extends LidoSDKCacheable { const lidoLocator = this.getContractLidoLocator(); if (contract === 'wsteth') { const withdrawalQueue = await lidoLocator.read.withdrawalQueue(); - const contract = this.getContractWQ(withdrawalQueue); - const wstethAddress = await contract.read.WSTETH(); + + const wstethAddress = await readContract(this.rpcProvider, { + abi: wqWstethAddressAbi, + address: withdrawalQueue, + functionName: 'WSTETH', + }); return wstethAddress; } else { @@ -386,6 +387,24 @@ export default class LidoSDKCore extends LidoSDKCacheable { } } + @Logger('Utils:') + @Cache(30 * 60 * 1000, ['chain.id']) + public getL2ContractAddress(contract: LIDO_L2_CONTRACT_NAMES): Address { + const chainConfig = LIDO_L2_CONTRACT_ADDRESSES[this.chain.id]; + invariant( + chainConfig, + `Lido L2 contracts are not supported for ${this.chain.name}(${this.chain.id})`, + ERROR_CODE.NOT_SUPPORTED, + ); + const address = chainConfig[contract]; + invariant( + address, + `Lido L2 on ${this.chain.name}(${this.chain.id}) does not have ${contract} contract`, + ERROR_CODE.NOT_SUPPORTED, + ); + return address; + } + @Logger('Utils:') @Cache(30 * 60 * 1000, ['chain.id']) public getSubgraphId(): string | null { From e4017e945eee701db416b8b9ba034368240043ce Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 12 Sep 2024 17:40:10 +0700 Subject: [PATCH 02/28] fix: abi exports --- packages/sdk/src/erc20/index.ts | 1 + packages/sdk/src/events/index.ts | 1 + packages/sdk/src/shares/index.ts | 1 + packages/sdk/src/stake/index.ts | 1 + packages/sdk/src/unsteth/index.ts | 1 + packages/sdk/src/withdraw/index.ts | 1 + packages/sdk/src/wrap/index.ts | 1 + 7 files changed, 7 insertions(+) diff --git a/packages/sdk/src/erc20/index.ts b/packages/sdk/src/erc20/index.ts index 468bc773..17498525 100644 --- a/packages/sdk/src/erc20/index.ts +++ b/packages/sdk/src/erc20/index.ts @@ -1,3 +1,4 @@ +export { erc20abi } from './abi/erc20abi.js'; export { AbstractLidoSDKErc20 } from './erc20.js'; export { LidoSDKstETH } from './steth.js'; export { LidoSDKwstETH } from './wsteth.js'; diff --git a/packages/sdk/src/events/index.ts b/packages/sdk/src/events/index.ts index df5449fa..86bde4bf 100644 --- a/packages/sdk/src/events/index.ts +++ b/packages/sdk/src/events/index.ts @@ -1,2 +1,3 @@ +export { StethEventsAbi } from './abi/stethEvents.js'; export { LidoSDKEvents } from './events.js'; export * from './types.js'; diff --git a/packages/sdk/src/shares/index.ts b/packages/sdk/src/shares/index.ts index 3861cdb8..ca4f495f 100644 --- a/packages/sdk/src/shares/index.ts +++ b/packages/sdk/src/shares/index.ts @@ -1,2 +1,3 @@ +export { stethSharesAbi } from './abi/steth-shares-abi.js'; export { LidoSDKShares } from './shares.js'; export type { SharesTransferProps } from './types.js'; diff --git a/packages/sdk/src/stake/index.ts b/packages/sdk/src/stake/index.ts index 61793c44..62531b9d 100644 --- a/packages/sdk/src/stake/index.ts +++ b/packages/sdk/src/stake/index.ts @@ -1,2 +1,3 @@ +export { StethAbi } from './abi/steth.js'; export { LidoSDKStake } from './stake.js'; export type { StakeProps, StakeEncodeDataProps } from './types.js'; diff --git a/packages/sdk/src/unsteth/index.ts b/packages/sdk/src/unsteth/index.ts index 4d0f8a4b..c36ca1f2 100644 --- a/packages/sdk/src/unsteth/index.ts +++ b/packages/sdk/src/unsteth/index.ts @@ -1,3 +1,4 @@ +export { unstethAbi } from './abi/unsteth-abi.js'; export { LidoSDKUnstETH } from './unsteth.js'; export type { UnstethNFT, diff --git a/packages/sdk/src/withdraw/index.ts b/packages/sdk/src/withdraw/index.ts index 5b2ce111..54a9e2e0 100644 --- a/packages/sdk/src/withdraw/index.ts +++ b/packages/sdk/src/withdraw/index.ts @@ -1,3 +1,4 @@ +export { WithdrawalQueueAbi } from './abi/withdrawalQueue.js'; export { LidoSDKWithdraw } from './withdraw.js'; export type { ClaimRequestsProps, diff --git a/packages/sdk/src/wrap/index.ts b/packages/sdk/src/wrap/index.ts index fe4d762b..8f666594 100644 --- a/packages/sdk/src/wrap/index.ts +++ b/packages/sdk/src/wrap/index.ts @@ -1,2 +1,3 @@ +export { abi as WstethABI } from './abi/wsteth.js'; export { LidoSDKWrap } from './wrap.js'; export type { WrapProps, WrapPropsWithoutCallback } from './types.js'; From 1919e2a586dc8abc5991dfe18b249e9d1f6fb5c0 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 18 Sep 2024 19:51:45 +0700 Subject: [PATCH 03/28] feat: l2 module --- packages/sdk/package.json | 14 +- packages/sdk/src/core/core.ts | 2 +- packages/sdk/src/erc20/erc20.ts | 82 ++-- packages/sdk/src/erc20/types.ts | 2 +- packages/sdk/src/erc20/wsteth.ts | 2 + packages/sdk/src/l2/abi/brigedWsteth.ts | 265 ++++++++++++ packages/sdk/src/l2/abi/rebasableL2Steth.ts | 390 ++++++++++++++++++ packages/sdk/src/l2/index.ts | 11 + packages/sdk/src/l2/l2.ts | 291 +++++++++++++ packages/sdk/src/l2/tokens.ts | 258 ++++++++++++ packages/sdk/src/l2/types.ts | 32 ++ packages/sdk/src/sdk.ts | 4 + packages/sdk/src/shares/shares.ts | 36 +- packages/sdk/src/stake/types.ts | 2 +- packages/sdk/src/unsteth/unsteth.ts | 76 ++-- .../components/layout/header/headerWallet.tsx | 20 +- playground/demo/core/index.tsx | 1 + playground/demo/index.tsx | 5 + playground/demo/l2/index.tsx | 12 + playground/demo/l2/tokens.tsx | 16 + playground/demo/l2/wrap-l2.tsx | 124 ++++++ playground/demo/tokens/index.tsx | 2 +- playground/package.json | 4 +- playground/providers/sdk.tsx | 6 +- playground/providers/web3.tsx | 4 + yarn.lock | 265 +++++++----- 26 files changed, 1740 insertions(+), 186 deletions(-) create mode 100644 packages/sdk/src/l2/abi/brigedWsteth.ts create mode 100644 packages/sdk/src/l2/abi/rebasableL2Steth.ts create mode 100644 packages/sdk/src/l2/index.ts create mode 100644 packages/sdk/src/l2/l2.ts create mode 100644 packages/sdk/src/l2/tokens.ts create mode 100644 packages/sdk/src/l2/types.ts create mode 100644 playground/demo/l2/index.tsx create mode 100644 playground/demo/l2/tokens.tsx create mode 100644 playground/demo/l2/wrap-l2.tsx diff --git a/packages/sdk/package.json b/packages/sdk/package.json index da82685f..7ecb6639 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -71,6 +71,11 @@ "default": "./dist/cjs/shares/index.js", "types": "./dist/types/shares/index.d.ts" }, + "./l2": { + "import": "./dist/esm/l2/index.js", + "default": "./dist/cjs/l2/index.js", + "types": "./dist/types/l2/index.d.ts" + }, "./package.json": "./package.json" }, "typesVersions": { @@ -104,6 +109,9 @@ ], "shares": [ "./dist/types/shares/index.d.ts" + ], + "l2": [ + "./dist/types/l2/index.d.ts" ] } }, @@ -145,8 +153,10 @@ "dependencies": { "@ethersproject/bytes": "^5.7.0", "graphql": "^16.8.1", - "graphql-request": "^6.1.0", - "viem": "^2.0.6" + "graphql-request": "^6.1.0" + }, + "peerDependencies": { + "viem": "^2.21" }, "devDependencies": { "@jest/globals": "^29.7.0", diff --git a/packages/sdk/src/core/core.ts b/packages/sdk/src/core/core.ts index 518a3acb..cb73b325 100644 --- a/packages/sdk/src/core/core.ts +++ b/packages/sdk/src/core/core.ts @@ -357,7 +357,7 @@ export default class LidoSDKCore extends LidoSDKCacheable { // eth_getCode returns hex string of bytecode at address // for accounts it's "0x" // for contract it's potentially very long hex (can't be safely&quickly parsed) - const result = await this.rpcProvider.getBytecode({ address: address }); + const result = await this.rpcProvider.getCode({ address: address }); return result ? result !== '0x' : false; } diff --git a/packages/sdk/src/erc20/erc20.ts b/packages/sdk/src/erc20/erc20.ts index 135a2328..52f0c58d 100644 --- a/packages/sdk/src/erc20/erc20.ts +++ b/packages/sdk/src/erc20/erc20.ts @@ -121,7 +121,9 @@ export abstract class AbstractLidoSDKErc20 extends LidoSDKModule { const contract = await this.getContract(); return isTransferFrom - ? contract.simulate.transferFrom([from, to, amount], { account }) + ? contract.simulate.transferFrom([from, to, amount], { + account, + }) : contract.simulate.transfer([to, amount], { account }); } @@ -217,7 +219,9 @@ export abstract class AbstractLidoSDKErc20 extends LidoSDKModule { public async simulateApprove(props: NoCallback) { const { account, amount, to } = await this.parseProps(props); const contract = await this.getContract(); - return contract.simulate.approve([to, amount], { account }); + return contract.simulate.approve([to, amount], { + account, + }); } @Logger('Views:') @@ -239,35 +243,51 @@ export abstract class AbstractLidoSDKErc20 extends LidoSDKModule { decimals: number; domainSeparator: Hash; }> { - const contract = { address: await this.contractAddress(), abi: erc20abi }; - const [decimals, name, symbol, domainSeparator] = - await this.core.rpcProvider.multicall({ - allowFailure: false, - contracts: [ - { - ...contract, - functionName: 'decimals', - }, - { - ...contract, - functionName: 'name', - }, - { - ...contract, - functionName: 'symbol', - }, - { - ...contract, - functionName: 'DOMAIN_SEPARATOR', - }, - ] as const, - }); - return { - decimals, - name, - symbol, - domainSeparator, - }; + if (this.core.rpcProvider.multicall) { + const contract = { address: await this.contractAddress(), abi: erc20abi }; + const [decimals, name, symbol, domainSeparator] = + await this.core.rpcProvider.multicall({ + allowFailure: false, + contracts: [ + { + ...contract, + functionName: 'decimals', + }, + { + ...contract, + functionName: 'name', + }, + { + ...contract, + functionName: 'symbol', + }, + { + ...contract, + functionName: 'DOMAIN_SEPARATOR', + }, + ] as const, + }); + return { + decimals, + name, + symbol, + domainSeparator, + }; + } else { + const contract = await this.getContract(); + const [decimals, name, symbol, domainSeparator] = await Promise.all([ + contract.read.decimals(), + contract.read.name(), + contract.read.symbol(), + contract.read.DOMAIN_SEPARATOR(), + ]); + return { + decimals, + name, + symbol, + domainSeparator, + }; + } } @Logger('Views:') diff --git a/packages/sdk/src/erc20/types.ts b/packages/sdk/src/erc20/types.ts index d0fa0c04..3421020d 100644 --- a/packages/sdk/src/erc20/types.ts +++ b/packages/sdk/src/erc20/types.ts @@ -9,7 +9,7 @@ import { export type InnerTransactionProps = Required; export type ParsedTransactionProps = - Omit & { + Omit & { callback: NonNullable; account: JsonRpcAccount; } & (TProps extends { amount: EtherValue } ? { amount: bigint } : object); diff --git a/packages/sdk/src/erc20/wsteth.ts b/packages/sdk/src/erc20/wsteth.ts index 75723a43..e278e34a 100644 --- a/packages/sdk/src/erc20/wsteth.ts +++ b/packages/sdk/src/erc20/wsteth.ts @@ -12,6 +12,8 @@ export class LidoSDKwstETH extends AbstractLidoSDKErc20 { return this.core.getContractAddress(LIDO_CONTRACT_NAMES.wsteth); } + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) public override async erc721Domain(): Promise<{ name: string; version: string; diff --git a/packages/sdk/src/l2/abi/brigedWsteth.ts b/packages/sdk/src/l2/abi/brigedWsteth.ts new file mode 100644 index 00000000..c54530f8 --- /dev/null +++ b/packages/sdk/src/l2/abi/brigedWsteth.ts @@ -0,0 +1,265 @@ +export const bridgedWstethAbi = [ + { inputs: [], name: 'ErrorAccountIsZeroAddress', type: 'error' }, + { inputs: [], name: 'ErrorDeadlineExpired', type: 'error' }, + { inputs: [], name: 'ErrorInvalidSignature', type: 'error' }, + { inputs: [], name: 'ErrorMetadataIsAlreadyInitialized', type: 'error' }, + { inputs: [], name: 'ErrorMetadataIsNotInitialized', type: 'error' }, + { inputs: [], name: 'ErrorNameIsEmpty', type: 'error' }, + { inputs: [], name: 'ErrorNotBridge', type: 'error' }, + { inputs: [], name: 'ErrorNotEnoughAllowance', type: 'error' }, + { inputs: [], name: 'ErrorNotEnoughBalance', type: 'error' }, + { inputs: [], name: 'ErrorSymbolIsEmpty', type: 'error' }, + { inputs: [], name: 'ErrorZeroAddressBridge', type: 'error' }, + { inputs: [], name: 'ErrorZeroDecimals', type: 'error' }, + { inputs: [], name: 'InvalidContractVersionIncrement', type: 'error' }, + { inputs: [], name: 'NonZeroContractVersionOnInit', type: 'error' }, + { + inputs: [ + { internalType: 'uint256', name: 'expected', type: 'uint256' }, + { internalType: 'uint256', name: 'received', type: 'uint256' }, + ], + name: 'UnexpectedContractVersion', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'version', + type: 'uint256', + }, + ], + name: 'ContractVersionSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '', type: 'address' }, + { internalType: 'address', name: '', type: 'address' }, + ], + name: 'allowance', + 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: '', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'bridge', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'account_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'bridgeBurn', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'account_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'bridgeMint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'eip712Domain', + outputs: [ + { internalType: 'bytes1', name: 'fields', type: 'bytes1' }, + { internalType: 'string', name: 'name', type: 'string' }, + { internalType: 'string', name: 'version', type: 'string' }, + { internalType: 'uint256', name: 'chainId', type: 'uint256' }, + { internalType: 'address', name: 'verifyingContract', type: 'address' }, + { internalType: 'bytes32', name: 'salt', type: 'bytes32' }, + { internalType: 'uint256[]', name: 'extensions', type: 'uint256[]' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'string', name: 'name_', type: 'string' }, + { internalType: 'string', name: 'version_', type: 'string' }, + ], + name: 'finalizeUpgrade_v2', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getContractVersion', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'string', name: 'name_', type: 'string' }, + { internalType: 'string', name: 'symbol_', type: 'string' }, + { internalType: 'string', name: 'version_', type: 'string' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + 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: 'owner_', type: 'address' }, + { internalType: 'address', name: 'spender_', type: 'address' }, + { internalType: 'uint256', name: 'value_', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline_', type: 'uint256' }, + { internalType: 'bytes', name: 'signature_', type: 'bytes' }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'owner_', type: 'address' }, + { internalType: 'address', name: 'spender_', type: 'address' }, + { 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' }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'to_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'from_', type: 'address' }, + { internalType: 'address', name: 'to_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; diff --git a/packages/sdk/src/l2/abi/rebasableL2Steth.ts b/packages/sdk/src/l2/abi/rebasableL2Steth.ts new file mode 100644 index 00000000..50a48f53 --- /dev/null +++ b/packages/sdk/src/l2/abi/rebasableL2Steth.ts @@ -0,0 +1,390 @@ +export const rebasableL2StethAbi = [ + { + inputs: [ + { internalType: 'string', name: 'name_', type: 'string' }, + { internalType: 'string', name: 'symbol_', type: 'string' }, + { internalType: 'string', name: 'version_', type: 'string' }, + { internalType: 'uint8', name: 'decimals_', type: 'uint8' }, + { internalType: 'address', name: 'tokenToWrapFrom_', type: 'address' }, + { internalType: 'address', name: 'tokenRateOracle_', type: 'address' }, + { internalType: 'address', name: 'bridge_', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { inputs: [], name: 'ErrorAccountIsZeroAddress', type: 'error' }, + { inputs: [], name: 'ErrorDeadlineExpired', type: 'error' }, + { inputs: [], name: 'ErrorInvalidSignature', type: 'error' }, + { inputs: [], name: 'ErrorNameIsEmpty', type: 'error' }, + { inputs: [], name: 'ErrorNotBridge', type: 'error' }, + { inputs: [], name: 'ErrorNotEnoughAllowance', type: 'error' }, + { inputs: [], name: 'ErrorNotEnoughBalance', type: 'error' }, + { inputs: [], name: 'ErrorSymbolIsEmpty', type: 'error' }, + { inputs: [], name: 'ErrorTransferToRebasableContract', type: 'error' }, + { inputs: [], name: 'ErrorZeroAddressL2ERC20TokenBridge', type: 'error' }, + { inputs: [], name: 'ErrorZeroAddressTokenRateOracle', type: 'error' }, + { inputs: [], name: 'ErrorZeroAddressTokenToWrapFrom', type: 'error' }, + { inputs: [], name: 'ErrorZeroDecimals', type: 'error' }, + { inputs: [], name: 'ErrorZeroSharesUnwrap', type: 'error' }, + { inputs: [], name: 'ErrorZeroSharesWrap', type: 'error' }, + { inputs: [], name: 'ErrorZeroTokensUnwrap', type: 'error' }, + { inputs: [], name: 'InvalidContractVersionIncrement', type: 'error' }, + { inputs: [], name: 'NonZeroContractVersionOnInit', type: 'error' }, + { + inputs: [ + { internalType: 'uint256', name: 'expected', type: 'uint256' }, + { internalType: 'uint256', name: 'received', type: 'uint256' }, + ], + name: 'UnexpectedContractVersion', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'version', + type: 'uint256', + }, + ], + name: 'ContractVersionSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'from', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { + indexed: false, + internalType: 'uint256', + name: 'sharesValue', + type: 'uint256', + }, + ], + name: 'TransferShares', + type: 'event', + }, + { + inputs: [], + name: 'DOMAIN_SEPARATOR', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'L2_ERC20_TOKEN_BRIDGE', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'TOKEN_RATE_ORACLE', + outputs: [ + { internalType: 'contract ITokenRateOracle', name: '', type: 'address' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'TOKEN_RATE_ORACLE_DECIMALS', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'TOKEN_TO_WRAP_FROM', + outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }], + stateMutability: 'view', + 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', + }, + { + 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: 'account_', type: 'address' }], + name: 'balanceOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'account_', type: 'address' }, + { internalType: 'uint256', name: 'tokenAmount_', type: 'uint256' }, + ], + name: 'bridgeUnwrap', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'account_', type: 'address' }, + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'bridgeWrap', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'eip712Domain', + outputs: [ + { internalType: 'bytes1', name: 'fields', type: 'bytes1' }, + { internalType: 'string', name: 'name', type: 'string' }, + { internalType: 'string', name: 'version', type: 'string' }, + { internalType: 'uint256', name: 'chainId', type: 'uint256' }, + { internalType: 'address', name: 'verifyingContract', type: 'address' }, + { internalType: 'bytes32', name: 'salt', type: 'bytes32' }, + { internalType: 'uint256[]', name: 'extensions', type: 'uint256[]' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getContractVersion', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'tokenAmount_', type: 'uint256' }, + ], + name: 'getSharesByTokens', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'getTokensByShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getTotalShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'string', name: 'name_', type: 'string' }, + { internalType: 'string', name: 'symbol_', type: 'string' }, + { internalType: 'string', name: 'version_', type: 'string' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + 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: 'owner_', type: 'address' }, + { internalType: 'address', name: 'spender_', type: 'address' }, + { internalType: 'uint256', name: 'value_', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline_', type: 'uint256' }, + { internalType: 'bytes', name: 'signature_', type: 'bytes' }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'owner_', type: 'address' }, + { internalType: 'address', name: 'spender_', type: 'address' }, + { 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' }, + ], + name: 'permit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'account_', type: 'address' }], + name: 'sharesOf', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'to_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'from_', type: 'address' }, + { internalType: 'address', name: 'to_', type: 'address' }, + { internalType: 'uint256', name: 'amount_', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient_', type: 'address' }, + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'transferShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'sender_', type: 'address' }, + { internalType: 'address', name: 'recipient_', type: 'address' }, + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'transferSharesFrom', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'tokenAmount_', type: 'uint256' }, + ], + name: 'unwrap', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'unwrapShares', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'sharesAmount_', type: 'uint256' }, + ], + name: 'wrap', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; diff --git a/packages/sdk/src/l2/index.ts b/packages/sdk/src/l2/index.ts new file mode 100644 index 00000000..f4c8c6bd --- /dev/null +++ b/packages/sdk/src/l2/index.ts @@ -0,0 +1,11 @@ +export { bridgedWstethAbi } from './abi/brigedWsteth.js'; +export { rebasableL2StethAbi } from './abi/rebasableL2Steth.js'; +export { LidoSDKL2 } from './l2.js'; +export { LidoSDKL2Wsteth, LidoSDKL2Steth } from './tokens.js'; + +export type { + SharesTransferProps, + UnwrapResults, + WrapProps, + WrapResults, +} from './types.js'; diff --git a/packages/sdk/src/l2/l2.ts b/packages/sdk/src/l2/l2.ts new file mode 100644 index 00000000..a8e1cca8 --- /dev/null +++ b/packages/sdk/src/l2/l2.ts @@ -0,0 +1,291 @@ +import { LidoSDKModule } from '../common/class-primitives/sdk-module.js'; +import type { AccountValue, LidoSDKCommonProps } from '../core/types.js'; +import { LidoSDKL2Steth, LidoSDKL2Wsteth } from './tokens.js'; + +import { + getContract, + encodeFunctionData, + decodeEventLog, + isAddressEqual, + getAbiItem, + toEventHash, + type GetContractReturnType, + type WalletClient, + type Address, + type WriteContractParameters, + type TransactionReceipt, +} from 'viem'; + +import { LIDO_L2_CONTRACT_NAMES, NOOP } from '../common/constants.js'; +import { Cache, Logger, ErrorHandler } from '../common/decorators/index.js'; + +import { rebasableL2StethAbi } from './abi/rebasableL2Steth.js'; +import { parseValue } from '../common/utils/parse-value.js'; +import { + UnwrapResults, + WrapInnerProps, + WrapProps, + WrapPropsWithoutCallback, + WrapResults, +} from './types.js'; +import { PopulatedTransaction, TransactionResult } from '../core/types.js'; +import { invariant, ERROR_CODE } from '../common/index.js'; + +export class LidoSDKL2 extends LidoSDKModule { + private static TRANSFER_SIGNATURE = toEventHash( + getAbiItem({ abi: rebasableL2StethAbi, name: 'Transfer' }), + ); + + public readonly wsteth: LidoSDKL2Wsteth; + public readonly steth: LidoSDKL2Steth; + + constructor(props: LidoSDKCommonProps) { + super(props); + this.wsteth = new LidoSDKL2Wsteth({ core: this.core }); + this.steth = new LidoSDKL2Steth({ core: this.core }); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async contractAddress(): Promise
{ + return this.core.getL2ContractAddress(LIDO_L2_CONTRACT_NAMES.steth); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async getContract(): Promise< + GetContractReturnType + > { + const address = await this.contractAddress(); + return getContract({ + address, + abi: rebasableL2StethAbi, + client: { + public: this.core.rpcProvider, + wallet: this.core.web3Provider as WalletClient, + }, + }); + } + + // Wrap wstETH to stETH + + @Logger('Call:') + @ErrorHandler() + public async approveWsethForWrap( + props: WrapProps, + ): Promise { + const stethAddress = await this.contractAddress(); + return this.wsteth.approve({ + ...props, + amount: props.value, + to: stethAddress, + }); + } + + @Logger('Utils:') + @ErrorHandler() + public async getWstethForWrapAllowance( + account?: AccountValue, + ): Promise { + const stethAddress = await this.contractAddress(); + return this.wsteth.allowance({ account, to: stethAddress }); + } + + @Logger('Call:') + @ErrorHandler() + public async wrapWstethToSteth( + props: WrapProps, + ): Promise> { + this.core.useWeb3Provider(); + const { account, callback, value, ...rest } = await this.parseProps(props); + const contract = await this.getContract(); + + return this.core.performTransaction({ + ...rest, + account, + callback, + getGasLimit: (options) => contract.estimateGas.wrap([value], options), + sendTransaction: (options) => contract.write.wrap([value], options), + decodeResult: (receipt) => + this.wrapWstethToStethParseEvents(receipt, account.address), + }); + } + + @Logger('Utils:') + public async wrapWstethToStethPopulateTx( + props: WrapPropsWithoutCallback, + ): Promise { + const { value, account } = await this.parseProps(props); + const address = await this.contractAddress(); + + return { + to: address, + from: account.address, + data: encodeFunctionData({ + abi: rebasableL2StethAbi, + functionName: 'wrap', + args: [value], + }), + }; + } + + @Logger('Utils:') + private async wrapWstethToStethParseEvents( + receipt: TransactionReceipt, + address: Address, + ): Promise { + let stethReceived: bigint | undefined; + let wstethWrapped: bigint | undefined; + for (const log of receipt.logs) { + // skips non-relevant events + if (log.topics[0] !== LidoSDKL2.TRANSFER_SIGNATURE) continue; + const parsedLog = decodeEventLog({ + abi: rebasableL2StethAbi, + strict: true, + eventName: 'Transfer', + data: log.data, + topics: log.topics, + }); + if (isAddressEqual(parsedLog.args.from, address)) { + wstethWrapped = parsedLog.args.value; + } else if (isAddressEqual(parsedLog.args.to, address)) { + stethReceived = parsedLog.args.value; + } + } + invariant( + stethReceived, + 'could not find Transfer event in wrap transaction', + ERROR_CODE.TRANSACTION_ERROR, + ); + invariant( + wstethWrapped, + 'could not find Transfer event in wrap transaction', + ERROR_CODE.TRANSACTION_ERROR, + ); + return { + stethReceived, + wstethWrapped, + }; + } + + @Logger('Call:') + @ErrorHandler() + public async wrapWstethToStethSimulateTx( + props: WrapPropsWithoutCallback, + ): Promise { + const { value, account } = await this.parseProps(props); + const contract = await this.getContract(); + + const { request } = await contract.simulate.wrap([value], { + account, + }); + + return request; + } + + // unwrap stETH to wstETH + + @Logger('Call:') + @ErrorHandler() + public async unwrap( + props: WrapProps, + ): Promise> { + this.core.useWeb3Provider(); + const { account, callback, value, ...rest } = await this.parseProps(props); + const contract = await this.getContract(); + + return this.core.performTransaction({ + ...rest, + account, + callback, + getGasLimit: (options) => contract.estimateGas.unwrap([value], options), + sendTransaction: (options) => contract.write.unwrap([value], options), + decodeResult: (receipt) => + this.unwrapParseEvents(receipt, account.address), + }); + } + + @Logger('Utils:') + public async unwrapPopulateTx( + props: Omit, + ): Promise { + const { value, account } = await this.parseProps(props); + const to = await this.contractAddress(); + + return { + to, + from: account.address, + data: encodeFunctionData({ + abi: rebasableL2StethAbi, + functionName: 'unwrap', + args: [value], + }), + }; + } + + @Logger('Call:') + @ErrorHandler() + public async unwrapSimulateTx( + props: Omit, + ): Promise { + const { value, account } = await this.parseProps(props); + const contract = await this.getContract(); + + const { request } = await contract.simulate.unwrap([value], { + account, + }); + + return request; + } + + @Logger('Utils:') + private async unwrapParseEvents( + receipt: TransactionReceipt, + address: Address, + ): Promise { + let stethUnwrapped: bigint | undefined; + let wstethReceived: bigint | undefined; + for (const log of receipt.logs) { + // skips non-relevant events + if (log.topics[0] !== LidoSDKL2.TRANSFER_SIGNATURE) continue; + const parsedLog = decodeEventLog({ + abi: rebasableL2StethAbi, + strict: true, + topics: log.topics, + data: log.data, + eventName: 'Transfer', + }); + if (isAddressEqual(parsedLog.args.from, address)) { + stethUnwrapped = parsedLog.args.value; + } else if (isAddressEqual(parsedLog.args.to, address)) { + wstethReceived = parsedLog.args.value; + } + } + invariant( + stethUnwrapped, + 'could not find Transfer event in unwrap transaction', + ERROR_CODE.TRANSACTION_ERROR, + ); + invariant( + wstethReceived, + 'could not find Transfer event in unwrap transaction', + ERROR_CODE.TRANSACTION_ERROR, + ); + return { + stethUnwrapped, + wstethReceived, + }; + } + + // Utils + + @Logger('Utils:') + private async parseProps(props: WrapProps): Promise { + return { + ...props, + account: await this.core.useAccount(props.account), + value: parseValue(props.value), + callback: props.callback ?? NOOP, + }; + } +} diff --git a/packages/sdk/src/l2/tokens.ts b/packages/sdk/src/l2/tokens.ts new file mode 100644 index 00000000..57e7039d --- /dev/null +++ b/packages/sdk/src/l2/tokens.ts @@ -0,0 +1,258 @@ +/* eslint-disable sonarjs/no-identical-functions */ +import { + getContract, + type GetContractReturnType, + type WalletClient, + type Address, + encodeFunctionData, + Hash, +} from 'viem'; + +import { LIDO_L2_CONTRACT_NAMES, NOOP } from '../common/constants.js'; +import { parseValue } from '../common/utils/parse-value.js'; +import { Cache, ErrorHandler, Logger } from '../common/decorators/index.js'; +import { AbstractLidoSDKErc20 } from '../erc20/erc20.js'; + +import { rebasableL2StethAbi } from './abi/rebasableL2Steth.js'; +import { bridgedWstethAbi } from './abi/brigedWsteth.js'; + +import type { + AccountValue, + EtherValue, + NoCallback, + TransactionOptions, + TransactionResult, +} from '../core/types.js'; +import type { SharesTransferProps } from './types.js'; + +export class LidoSDKL2Wsteth extends AbstractLidoSDKErc20 { + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) + public override async contractAddress(): Promise
{ + return this.core.getL2ContractAddress(LIDO_L2_CONTRACT_NAMES.wsteth); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async getL2Contract(): Promise< + GetContractReturnType + > { + const address = await this.contractAddress(); + return getContract({ + address, + abi: bridgedWstethAbi, + client: { + public: this.core.rpcProvider, + wallet: this.core.web3Provider as WalletClient, + }, + }); + } + + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async contractVersion(): Promise { + const contract = await this.getL2Contract(); + return contract.read.getContractVersion(); + } + + @Cache(30 * 60 * 1000, ['core.chain.id']) + public override async erc721Domain(): Promise<{ + name: string; + version: string; + chainId: bigint; + verifyingContract: `0x${string}`; + }> { + const { name } = await this.erc20Metadata(); + return { + name: name, + version: (await this.contractVersion()).toString(), + chainId: BigInt(this.core.chain.id), + verifyingContract: await this.contractAddress(), + }; + } +} + +export class LidoSDKL2Steth extends AbstractLidoSDKErc20 { + public override async contractAddress(): Promise
{ + return this.core.getL2ContractAddress(LIDO_L2_CONTRACT_NAMES.steth); + } + + @Logger('Contracts:') + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async getL2Contract(): Promise< + GetContractReturnType + > { + const address = await this.contractAddress(); + return getContract({ + address, + abi: rebasableL2StethAbi, + client: { + public: this.core.rpcProvider, + wallet: this.core.web3Provider as WalletClient, + }, + }); + } + + @Cache(30 * 60 * 1000, ['core.chain.id']) + public async contractVersion(): Promise { + const contract = await this.getL2Contract(); + return contract.read.getContractVersion(); + } + + @Cache(30 * 60 * 1000, ['core.chain.id']) + public override async erc721Domain(): Promise<{ + name: string; + version: string; + chainId: bigint; + verifyingContract: Address; + fields: Hash; + salt: Hash; + extensions: readonly bigint[]; + }> { + const contract = await this.getL2Contract(); + const [ + fields, + name, + version, + chainId, + verifyingContract, + salt, + extensions, + ] = await contract.read.eip712Domain(); + return { + fields, + name, + version, + chainId, + verifyingContract, + salt, + extensions, + }; + } + + @Logger('Balances:') + @ErrorHandler() + public async balanceShares(address?: AccountValue): Promise { + const contract = await this.getL2Contract(); + const account = await this.core.useAccount(address); + return contract.read.sharesOf([account.address]); + } + + // convert + @Logger('Views:') + @ErrorHandler() + public async convertToShares(stethAmount: EtherValue): Promise { + const amount = parseValue(stethAmount); + const contract = await this.getL2Contract(); + return contract.read.getSharesByTokens([amount]); + } + + // convert + @Logger('Views:') + @ErrorHandler() + public async convertToSteth(sharesAmount: EtherValue): Promise { + const amount = parseValue(sharesAmount); + const contract = await this.getL2Contract(); + return contract.read.getTokensByShares([amount]); + } + + // convert + @Logger('Views:') + @ErrorHandler() + public async totalShares(): Promise { + const contract = await this.getL2Contract(); + return contract.read.getTotalShares(); + } + + // TransferShares + // Transfer + + @Logger('Call:') + @ErrorHandler() + public async transferShares({ + account: accountProp, + to, + amount: _amount, + callback = NOOP, + from: _from, + ...rest + }: SharesTransferProps): Promise { + this.core.useWeb3Provider(); + const account = await this.core.useAccount(accountProp); + const from = _from ?? account.address; + const amount = parseValue(_amount); + + const isTransferFrom = from !== account.address; + const contract = await this.getL2Contract(); + + const getGasLimit = async (overrides: TransactionOptions) => + isTransferFrom + ? contract.estimateGas.transferSharesFrom([from, to, amount], overrides) + : contract.estimateGas.transferShares([to, amount], overrides); + + const sendTransaction = async (overrides: TransactionOptions) => + isTransferFrom + ? contract.write.transferSharesFrom([from, to, amount], overrides) + : contract.write.transferShares([to, amount], overrides); + + return this.core.performTransaction({ + ...rest, + account, + callback, + getGasLimit, + sendTransaction, + }); + } + + @Logger('Utils:') + @ErrorHandler() + public async populateTransferShares({ + account: accountProp, + to, + amount: _amount, + from: _from, + }: NoCallback) { + const account = await this.core.useAccount(accountProp); + const amount = parseValue(_amount); + const from = _from ?? account.address; + const address = await this.contractAddress(); + const isTransferFrom = from !== account.address; + + return { + to: address, + from: account.address, + data: isTransferFrom + ? encodeFunctionData({ + abi: rebasableL2StethAbi, + functionName: 'transferSharesFrom', + args: [from, to, amount], + }) + : encodeFunctionData({ + abi: rebasableL2StethAbi, + functionName: 'transferShares', + args: [to, amount], + }), + }; + } + + @Logger('Utils:') + @ErrorHandler() + public async simulateTransferShares({ + account: _account, + to, + amount: _amount, + from: _from, + }: NoCallback) { + const amount = parseValue(_amount); + const account = await this.core.useAccount(_account); + const from = _from ?? account.address; + const contract = await this.getL2Contract(); + const isTransferFrom = from !== account.address; + return isTransferFrom + ? contract.simulate.transferSharesFrom([from, to, amount], { + account, + }) + : contract.simulate.transferShares([to, amount], { + account, + }); + } +} diff --git a/packages/sdk/src/l2/types.ts b/packages/sdk/src/l2/types.ts new file mode 100644 index 00000000..d10f2c61 --- /dev/null +++ b/packages/sdk/src/l2/types.ts @@ -0,0 +1,32 @@ +import type { Address } from 'viem'; +import type { CommonTransactionProps, EtherValue } from '../core/types.js'; +import type { FormattedTransactionRequest, JsonRpcAccount } from 'viem'; + +export type SharesTransferProps = CommonTransactionProps & { + from?: Address; + to: Address; + amount: EtherValue; +}; + +export type WrapProps = CommonTransactionProps & { + value: EtherValue; +}; + +export type WrapResults = { + stethReceived: bigint; + wstethWrapped: bigint; +}; + +export type UnwrapResults = { + stethUnwrapped: bigint; + wstethReceived: bigint; +}; + +export type WrapPropsWithoutCallback = Omit; + +export type WrapInnerProps = Omit & { + value: bigint; + account: JsonRpcAccount; +}; + +export type PopulatedTx = Omit; diff --git a/packages/sdk/src/sdk.ts b/packages/sdk/src/sdk.ts index 3caf4c52..5420c5a4 100644 --- a/packages/sdk/src/sdk.ts +++ b/packages/sdk/src/sdk.ts @@ -11,6 +11,7 @@ import { LidoSDKRewards } from './rewards/index.js'; import { LidoSDKShares } from './shares/shares.js'; import { version } from './version.js'; +import { LidoSDKL2 } from './l2/l2.js'; export class LidoSDK { readonly core: LidoSDKCore; @@ -24,6 +25,7 @@ export class LidoSDK { readonly events: LidoSDKEvents; readonly statistics: LidoSDKStatistics; readonly rewards: LidoSDKRewards; + readonly l2: LidoSDKL2; constructor(props: LidoSDKCoreProps) { // Core functionality @@ -46,5 +48,7 @@ export class LidoSDK { this.statistics = new LidoSDKStatistics({ ...props, core }); // Rewards functionality this.rewards = new LidoSDKRewards({ ...props, core }); + // L2 functionality + this.l2 = new LidoSDKL2({ ...props, core }); } } diff --git a/packages/sdk/src/shares/shares.ts b/packages/sdk/src/shares/shares.ts index d3f46a00..677e0ce9 100644 --- a/packages/sdk/src/shares/shares.ts +++ b/packages/sdk/src/shares/shares.ts @@ -177,20 +177,28 @@ export class LidoSDKShares extends LidoSDKModule { address: sharesContract.address, abi: sharesContract.abi, }; - const [totalShares, totalEther] = await this.core.rpcProvider.multicall({ - allowFailure: false, - contracts: [ - { - ...contract, - functionName: 'getTotalShares', - }, - { - ...contract, - functionName: 'getTotalPooledEther', - }, - ] as const, - }); - return { totalEther, totalShares }; + if (this.core.rpcProvider.multicall) { + const [totalShares, totalEther] = await this.core.rpcProvider.multicall({ + allowFailure: false, + contracts: [ + { + ...contract, + functionName: 'getTotalShares', + }, + { + ...contract, + functionName: 'getTotalPooledEther', + }, + ] as const, + }); + return { totalEther, totalShares }; + } else { + const [totalShares, totalEther] = await Promise.all([ + sharesContract.read.getTotalShares(), + sharesContract.read.getTotalPooledEther(), + ]); + return { totalEther, totalShares }; + } } @Logger('Views:') diff --git a/packages/sdk/src/stake/types.ts b/packages/sdk/src/stake/types.ts index 11d34545..be9f00f7 100644 --- a/packages/sdk/src/stake/types.ts +++ b/packages/sdk/src/stake/types.ts @@ -7,7 +7,7 @@ export type StakeProps = CommonTransactionProps & { referralAddress?: Address; }; -export type StakeInnerProps = CommonTransactionProps & { +export type StakeInnerProps = Omit & { value: bigint; referralAddress: Address; account: JsonRpcAccount; diff --git a/packages/sdk/src/unsteth/unsteth.ts b/packages/sdk/src/unsteth/unsteth.ts index 4a0ddfaa..b540161d 100644 --- a/packages/sdk/src/unsteth/unsteth.ts +++ b/packages/sdk/src/unsteth/unsteth.ts @@ -281,36 +281,52 @@ export class LidoSDKUnstETH extends LidoSDKModule { @ErrorHandler() @Cache(30 * 60 * 1000, ['core.chain.id']) public async getContractMetadata() { - const address = await this.contractAddress(); - const common = { abi: unstethAbi, address } as const; - const [name, version, symbol, baseURI] = - await this.core.rpcProvider.multicall({ - allowFailure: false, - contracts: [ - { - ...common, - functionName: 'name', - }, - { - ...common, - functionName: 'getContractVersion', - }, - { - ...common, - functionName: 'symbol', - }, - { - ...common, - functionName: 'getBaseURI', - }, - ] as const, - }); - return { - name, - version, - symbol, - baseURI, - }; + if (this.core.rpcProvider.multicall) { + const address = await this.contractAddress(); + const common = { abi: unstethAbi, address } as const; + const [name, version, symbol, baseURI] = + await this.core.rpcProvider.multicall({ + allowFailure: false, + contracts: [ + { + ...common, + functionName: 'name', + }, + { + ...common, + functionName: 'getContractVersion', + }, + { + ...common, + functionName: 'symbol', + }, + { + ...common, + functionName: 'getBaseURI', + }, + ] as const, + }); + return { + name, + version, + symbol, + baseURI, + }; + } else { + const contract = await this.getContract(); + const [name, version, symbol, baseURI] = await Promise.all([ + contract.read.name(), + contract.read.getContractVersion(), + contract.read.symbol(), + contract.read.getBaseURI(), + ]); + return { + name, + version, + symbol, + baseURI, + }; + } } @Logger('Views:') diff --git a/playground/components/layout/header/headerWallet.tsx b/playground/components/layout/header/headerWallet.tsx index 1bd09c0b..b4526ba8 100644 --- a/playground/components/layout/header/headerWallet.tsx +++ b/playground/components/layout/header/headerWallet.tsx @@ -1,5 +1,5 @@ import { FC } from 'react'; -import { CHAINS, getChainColor } from '@lido-sdk/constants'; +import { getChainColor } from '@lido-sdk/constants'; import { useWeb3 } from 'reef-knot/web3-react'; import { ThemeToggler } from '@lidofinance/lido-ui'; @@ -9,15 +9,27 @@ import WalletConnect from 'components/layout/header/walletConnect'; import { HeaderWalletChainStyle } from './headerWalletStyles'; +import { useChains } from 'wagmi'; + +const tryGetColor = (chainId: number) => { + try { + return getChainColor(chainId); + } catch { + return '#FFFFFF'; + } +}; + const HeaderWallet: FC = () => { const { active, chainId } = useWeb3(); - const chainName = chainId && CHAINS[chainId]; + const chains = useChains(); + + const currentChain = chains.find((chain) => chain.id === chainId); return ( <> {chainId && ( - - {chainName} + + {currentChain?.name} )} {active ? : } diff --git a/playground/demo/core/index.tsx b/playground/demo/core/index.tsx index a8fe0408..ea916ac4 100644 --- a/playground/demo/core/index.tsx +++ b/playground/demo/core/index.tsx @@ -20,6 +20,7 @@ export const CoreDemo = () => { const dateAtTimestamp = new Date( Number(timestampSeconds) * 1000, ).toLocaleString(locale); + const getStethContract = useCallback(async () => { const address = await stake.contractAddressStETH(); setContractAddress(address); diff --git a/playground/demo/index.tsx b/playground/demo/index.tsx index 09a1dc95..9f264e0d 100644 --- a/playground/demo/index.tsx +++ b/playground/demo/index.tsx @@ -13,8 +13,13 @@ import { EventsDemo } from './events'; import { StatisticsDemo } from './statistics'; import { RewardsDemo } from './rewards'; import { ShareDemo } from './shares'; +import { useChainId } from 'wagmi'; +import { L2_CHAINS } from 'providers/web3'; +import { L2 } from './l2'; export const Demo = () => { + const chain = useChainId(); + if (L2_CHAINS.includes(chain)) return ; return ( <> diff --git a/playground/demo/l2/index.tsx b/playground/demo/l2/index.tsx new file mode 100644 index 00000000..46edd4de --- /dev/null +++ b/playground/demo/l2/index.tsx @@ -0,0 +1,12 @@ +import { StethL2Demo, WstethL2Demo } from './tokens'; +import { WrapL2Demo } from './wrap-l2'; + +export const L2 = () => { + return ( + <> + + + + + ); +}; diff --git a/playground/demo/l2/tokens.tsx b/playground/demo/l2/tokens.tsx new file mode 100644 index 00000000..618e2ed7 --- /dev/null +++ b/playground/demo/l2/tokens.tsx @@ -0,0 +1,16 @@ +import { TokenDemo } from 'demo/tokens'; +import { useLidoSDK } from 'providers/sdk'; + +export const WstethL2Demo = () => { + const { l2 } = useLidoSDK(); + return ; +}; + +export const StethL2Demo = () => { + const { l2 } = useLidoSDK(); + return ( + <> + + + ); +}; diff --git a/playground/demo/l2/wrap-l2.tsx b/playground/demo/l2/wrap-l2.tsx new file mode 100644 index 00000000..3a0eb586 --- /dev/null +++ b/playground/demo/l2/wrap-l2.tsx @@ -0,0 +1,124 @@ +import { Accordion } from '@lidofinance/lido-ui'; + +import { Action, renderTokenResult } from 'components/action'; +import { DEFAULT_VALUE, ValueType } from 'components/tokenInput'; +import TokenInput from 'components/tokenInput/tokenInput'; +import { useLidoSDK } from 'providers/sdk'; +import { useState } from 'react'; +import { transactionToast } from 'utils/transaction-toast'; + +const ZERO = BigInt(0); + +export const WrapL2Demo = () => { + const [wstethValue, setWstethValue] = useState(DEFAULT_VALUE); + const [stethValue, setStethValue] = useState(DEFAULT_VALUE); + + const { l2 } = useLidoSDK(); + + return ( + + l2.wsteth.balance()} + /> + l2.steth.balance()} + /> + l2.getWstethForWrapAllowance()} + /> + + l2.approveWsethForWrap({ + value: wstethValue ?? ZERO, + + callback: transactionToast, + }) + } + > + + + + l2.wrapWstethToSteth({ + value: wstethValue ?? ZERO, + + callback: transactionToast, + }) + } + /> + + l2.wrapWstethToStethPopulateTx({ + value: wstethValue ?? ZERO, + }) + } + /> + + l2.wrapWstethToStethSimulateTx({ + value: wstethValue ?? ZERO, + }) + } + /> + + + l2.unwrap({ + value: stethValue ?? ZERO, + + callback: transactionToast, + }) + } + > + + + + l2.unwrapPopulateTx({ + value: stethValue ?? ZERO, + }) + } + /> + + l2.unwrapSimulateTx({ + value: stethValue ?? ZERO, + }) + } + /> + + ); +}; diff --git a/playground/demo/tokens/index.tsx b/playground/demo/tokens/index.tsx index 8df64b0b..670ec9b1 100644 --- a/playground/demo/tokens/index.tsx +++ b/playground/demo/tokens/index.tsx @@ -25,7 +25,7 @@ export const WstethDemo = () => { return ; }; -const TokenDemo = ({ instance, name }: TokenDemoProps) => { +export const TokenDemo = ({ instance, name }: TokenDemoProps) => { const { account: web3account = '0x0' } = useWeb3(); const [transferAmountState, setTransferAmount] = useState(DEFAULT_VALUE); diff --git a/playground/package.json b/playground/package.json index c11a821a..3cab4ac1 100644 --- a/playground/package.json +++ b/playground/package.json @@ -34,8 +34,8 @@ "styled-components": "^5.3.5", "swr": "^1.3.0", "tiny-invariant": "^1.3.1", - "viem": "^2.0.6", - "wagmi": "2.11.2" + "viem": "^2.21.9", + "wagmi": "2.12.12" }, "devDependencies": { "@next/bundle-analyzer": "^13.4.19", diff --git a/playground/providers/sdk.tsx b/playground/providers/sdk.tsx index fd0dd51c..3704a598 100644 --- a/playground/providers/sdk.tsx +++ b/playground/providers/sdk.tsx @@ -2,7 +2,7 @@ import { createContext, useMemo, PropsWithChildren, useContext } from 'react'; import { LidoSDK } from '@lidofinance/lido-ethereum-sdk'; import invariant from 'tiny-invariant'; -import { useClient, useConnectorClient } from 'wagmi'; +import { usePublicClient, useWalletClient } from 'wagmi'; const context = createContext(null); @@ -13,9 +13,9 @@ export const useLidoSDK = () => { }; export const LidoSDKProvider: React.FC = ({ children }) => { - const publicClient = useClient(); + const publicClient = usePublicClient(); const chainId = publicClient?.chain.id; - const { data: walletClient } = useConnectorClient(); + const { data: walletClient } = useWalletClient(); const value = useMemo(() => { const sdk = new LidoSDK({ chainId: chainId as any, diff --git a/playground/providers/web3.tsx b/playground/providers/web3.tsx index 99c4fffc..b246f0f4 100644 --- a/playground/providers/web3.tsx +++ b/playground/providers/web3.tsx @@ -24,6 +24,8 @@ import { useThemeToggle } from '@lidofinance/lido-ui'; type ChainsList = [wagmiChains.Chain, ...wagmiChains.Chain[]]; +export const L2_CHAINS = [10, 11155420]; + const wagmiChainsArray = Object.values(wagmiChains) as any as ChainsList; const supportedChains = wagmiChainsArray.filter((chain) => @@ -63,6 +65,8 @@ const Web3Provider: FC = ({ children }) => { [CHAINS.Goerli]: getRpc(CHAINS.Goerli), [CHAINS.Holesky]: getRpc(CHAINS.Holesky), [CHAINS.Sepolia]: getRpc(CHAINS.Sepolia), + // OP sepolia + [11155420]: getRpc(11155420), }; }, [customRpc]); diff --git a/yarn.lock b/yarn.lock index b9d50ced..f13800c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5097,7 +5097,8 @@ __metadata: rimraf: ^5.0.1 ts-jest: ^29.1.2 typescript: ^5.4.5 - viem: ^2.0.6 + peerDependencies: + viem: ^2.21 languageName: unknown linkType: soft @@ -5363,9 +5364,9 @@ __metadata: languageName: node linkType: hard -"@metamask/sdk-communication-layer@npm:0.26.4": - version: 0.26.4 - resolution: "@metamask/sdk-communication-layer@npm:0.26.4" +"@metamask/sdk-communication-layer@npm:0.28.2": + version: 0.28.2 + resolution: "@metamask/sdk-communication-layer@npm:0.28.2" dependencies: bufferutil: ^4.0.8 date-fns: ^2.29.3 @@ -5378,13 +5379,13 @@ __metadata: eventemitter2: ^6.4.7 readable-stream: ^3.6.2 socket.io-client: ^4.5.1 - checksum: a355826fb3b91af1e52e6afb53590441765b4b47c06047d5e9330e90ab8c9e576f5a12a9ed72782de274632c6751326c3b7d895c5b9b0c85f2008510d2a382fb + checksum: 349103ca72018fc4077ddf3d84d3976572525cf27b17b65308c6a00710c66f06d2d3880bb611a4a6462d1fb54bc59edd6855826f78796f49d8c7cd9904742577 languageName: node linkType: hard -"@metamask/sdk-install-modal-web@npm:0.26.5": - version: 0.26.5 - resolution: "@metamask/sdk-install-modal-web@npm:0.26.5" +"@metamask/sdk-install-modal-web@npm:0.28.1": + version: 0.28.1 + resolution: "@metamask/sdk-install-modal-web@npm:0.28.1" dependencies: qr-code-styling: ^1.6.0-rc.1 peerDependencies: @@ -5399,19 +5400,20 @@ __metadata: optional: true react-native: optional: true - checksum: 3aba8d39bac0d320727b7abfcd803d843906962cea28685e946acba3f198b14d0a672ac8dfd6ec97a7d0efd4f92d00dae956af8872ca4cb2e677f91342bc58b0 + checksum: 8ee147c63927323105bdf7d76667c06618119b30b355543a74f3a08e7559448d217bdf9a4fee0900efa0fc3f5a13f6376a76b2679e0b8322f6811789868dce42 languageName: node linkType: hard -"@metamask/sdk@npm:0.26.5": - version: 0.26.5 - resolution: "@metamask/sdk@npm:0.26.5" +"@metamask/sdk@npm:0.28.4": + version: 0.28.4 + resolution: "@metamask/sdk@npm:0.28.4" dependencies: "@metamask/onboarding": ^1.0.1 "@metamask/providers": 16.1.0 - "@metamask/sdk-communication-layer": 0.26.4 - "@metamask/sdk-install-modal-web": 0.26.5 + "@metamask/sdk-communication-layer": 0.28.2 + "@metamask/sdk-install-modal-web": 0.28.1 "@types/dom-screen-wake-lock": ^1.0.0 + "@types/uuid": ^10.0.0 bowser: ^2.9.0 cross-fetch: ^4.0.0 debug: ^4.3.4 @@ -5437,7 +5439,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 63d53041a44669fd3110647a485e77e43038806c4548d86716e973c22b9f6ad019499beb9419cb8b08f243828aa3753485ba121d89169154462c90828f13f07f + checksum: 419880a65c09a340dac6ca756ef81193dbc441db6fab620a3bd610fa077b11b2dec96f4651e81aac49c266ac00dbd109ffc184e067738ded7ea325174be94f31 languageName: node linkType: hard @@ -5694,6 +5696,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.4.0": + version: 1.6.0 + resolution: "@noble/curves@npm:1.6.0" + dependencies: + "@noble/hashes": 1.5.0 + checksum: 258f3feb2a6098cf35521562ecb7d452fd728e8a008ff9f1ef435184f9d0c782ceb8f7b7fa8df3317c3be7a19f53995ee124cd05c8080b130bd42e3cb072f24d + languageName: node + linkType: hard + "@noble/curves@npm:~1.4.0": version: 1.4.2 resolution: "@noble/curves@npm:1.4.2" @@ -5724,6 +5735,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.5.0": + version: 1.5.0 + resolution: "@noble/hashes@npm:1.5.0" + checksum: 9cc031d5c888c455bfeef76af649b87f75380a4511405baea633c1e4912fd84aff7b61e99716f0231d244c9cfeda1fafd7d718963e6a0c674ed705e9b1b4f76b + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -6702,6 +6720,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.8": + version: 1.1.9 + resolution: "@scure/base@npm:1.1.9" + checksum: 120820a37dfe9dfe4cab2b7b7460552d08e67dee8057ed5354eb68d8e3440890ae983ce3bee957d2b45684950b454a2b6d71d5ee77c1fd3fddc022e2a510337f + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.2": version: 1.3.2 resolution: "@scure/bip32@npm:1.3.2" @@ -6765,6 +6790,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip39@npm:1.4.0" + dependencies: + "@noble/hashes": ~1.5.0 + "@scure/base": ~1.1.8 + checksum: 211f2c01361993bfe54c0e4949f290224381457c7f76d7cd51d6a983f3f4b6b9f85adfd0e623977d777ed80417a5fe729eb19dd34e657147810a0e58a8e7b9e0 + languageName: node + linkType: hard + "@semantic-release/commit-analyzer@npm:^10.0.0": version: 10.0.4 resolution: "@semantic-release/commit-analyzer@npm:10.0.4" @@ -8357,6 +8392,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "@types/uuid@npm:10.0.0" + checksum: e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944 + languageName: node + linkType: hard + "@types/ws@npm:^8.5.5": version: 8.5.10 resolution: "@types/ws@npm:8.5.10" @@ -8573,31 +8615,31 @@ __metadata: languageName: node linkType: hard -"@wagmi/connectors@npm:5.0.26": - version: 5.0.26 - resolution: "@wagmi/connectors@npm:5.0.26" +"@wagmi/connectors@npm:5.1.11": + version: 5.1.11 + resolution: "@wagmi/connectors@npm:5.1.11" dependencies: "@coinbase/wallet-sdk": 4.0.4 - "@metamask/sdk": 0.26.5 + "@metamask/sdk": 0.28.4 "@safe-global/safe-apps-provider": 0.18.3 "@safe-global/safe-apps-sdk": 9.1.0 - "@walletconnect/ethereum-provider": 2.13.0 + "@walletconnect/ethereum-provider": 2.16.1 "@walletconnect/modal": 2.6.2 cbw-sdk: "npm:@coinbase/wallet-sdk@3.9.3" peerDependencies: - "@wagmi/core": 2.12.2 + "@wagmi/core": 2.13.5 typescript: ">=5.0.4" viem: 2.x peerDependenciesMeta: typescript: optional: true - checksum: 286c641c912fb2d4620fa867d920d79f5730372ae49f26eff5484b36a1d21f03c984fcde9d85d4f2c732f2891bebc579e3d7613702b9ced8426cc7f10627c118 + checksum: c05beff4aa98dced7ba070dc2a2f9144f48026c19725c1e9fe04c843c4add0a4935de0aa8fddb8cb9d4cf2454d6504bfcfb2fa3bd6e5bb166e9b8d13eddfa087 languageName: node linkType: hard -"@wagmi/core@npm:2.12.2": - version: 2.12.2 - resolution: "@wagmi/core@npm:2.12.2" +"@wagmi/core@npm:2.13.5": + version: 2.13.5 + resolution: "@wagmi/core@npm:2.13.5" dependencies: eventemitter3: 5.0.1 mipd: 0.0.7 @@ -8611,13 +8653,13 @@ __metadata: optional: true typescript: optional: true - checksum: 06d3fde29b53474805460474158dfe7e3d6356dc11cf8f90033a9edae605cbe56839da7cbc20bb82e72d1ee68cf7b02ef493aa5d4f7041b8247017e89879b52e + checksum: e0fce057b613a36e35f43ceaaea3f300d92e37a53590d767cc4934035e0fff36dfe5c8ec64486fc78b3a11abcb10b0ac0b65f369ea0034afb5932572cb6f1187 languageName: node linkType: hard -"@walletconnect/core@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/core@npm:2.13.0" +"@walletconnect/core@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/core@npm:2.16.1" dependencies: "@walletconnect/heartbeat": 1.2.2 "@walletconnect/jsonrpc-provider": 1.0.14 @@ -8626,17 +8668,16 @@ __metadata: "@walletconnect/jsonrpc-ws-connection": 1.0.14 "@walletconnect/keyvaluestorage": 1.1.1 "@walletconnect/logger": 2.1.2 - "@walletconnect/relay-api": 1.0.10 + "@walletconnect/relay-api": 1.0.11 "@walletconnect/relay-auth": 1.0.4 "@walletconnect/safe-json": 1.0.2 "@walletconnect/time": 1.0.2 - "@walletconnect/types": 2.13.0 - "@walletconnect/utils": 2.13.0 + "@walletconnect/types": 2.16.1 + "@walletconnect/utils": 2.16.1 events: 3.3.0 - isomorphic-unfetch: 3.1.0 lodash.isequal: 4.5.0 uint8arrays: 3.1.0 - checksum: 6e503bdc7d678ccaeaa9d93fdc6311298d326febef87f233b80c12340178ae3eff54a3a79e19400d65298f109466c508dbef0d5710fffd09d357b7b6bec8b56f + checksum: ab658833fb845624ccb2d109c5218f5b4624b948b4a8b01bf22eb964c5e5d3cd890ee43645e196ad73d1be9a25d6a1bcd16a83893266ee1b30c9ec5377086820 languageName: node linkType: hard @@ -8649,21 +8690,21 @@ __metadata: languageName: node linkType: hard -"@walletconnect/ethereum-provider@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/ethereum-provider@npm:2.13.0" +"@walletconnect/ethereum-provider@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/ethereum-provider@npm:2.16.1" dependencies: "@walletconnect/jsonrpc-http-connection": 1.0.8 "@walletconnect/jsonrpc-provider": 1.0.14 "@walletconnect/jsonrpc-types": 1.0.4 "@walletconnect/jsonrpc-utils": 1.0.8 "@walletconnect/modal": 2.6.2 - "@walletconnect/sign-client": 2.13.0 - "@walletconnect/types": 2.13.0 - "@walletconnect/universal-provider": 2.13.0 - "@walletconnect/utils": 2.13.0 + "@walletconnect/sign-client": 2.16.1 + "@walletconnect/types": 2.16.1 + "@walletconnect/universal-provider": 2.16.1 + "@walletconnect/utils": 2.16.1 events: 3.3.0 - checksum: 24356a41b72fea5125ef0e6605df4469f023141ce5de8cc92f1ae23b35215efb0ee2c1e5857f483f34eccd4a051915b64518daadc4c8a2145bf91473c2f5a7bc + checksum: f88c6e41b7718a75452795efa7a1b51f5286aa728358a6b5f477e26f9c37e6cf5df1ec69fa227fe67674be53c2871296d767becbc23fc287ca6298a12d2c4984 languageName: node linkType: hard @@ -8811,12 +8852,12 @@ __metadata: languageName: node linkType: hard -"@walletconnect/relay-api@npm:1.0.10": - version: 1.0.10 - resolution: "@walletconnect/relay-api@npm:1.0.10" +"@walletconnect/relay-api@npm:1.0.11": + version: 1.0.11 + resolution: "@walletconnect/relay-api@npm:1.0.11" dependencies: "@walletconnect/jsonrpc-types": ^1.0.2 - checksum: a332cbfdf0d3bad7046b0559653a5121a4b5a540f029cc01eeb8ef466681b10626a5a24d55668405e7c635535f35b8038d4aa5a2f0d16c8b512c41fecff2448c + checksum: 9fcddf055de01c04b9fa59035e8c6e31d523743c848d266f528009048aeadaa1b4d9b544bdcb6928e7a69f738d5f0352d1cdebbaa34b1346b937942cb5f6f144 languageName: node linkType: hard @@ -8843,20 +8884,20 @@ __metadata: languageName: node linkType: hard -"@walletconnect/sign-client@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/sign-client@npm:2.13.0" +"@walletconnect/sign-client@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/sign-client@npm:2.16.1" dependencies: - "@walletconnect/core": 2.13.0 + "@walletconnect/core": 2.16.1 "@walletconnect/events": 1.0.1 "@walletconnect/heartbeat": 1.2.2 "@walletconnect/jsonrpc-utils": 1.0.8 "@walletconnect/logger": 2.1.2 "@walletconnect/time": 1.0.2 - "@walletconnect/types": 2.13.0 - "@walletconnect/utils": 2.13.0 + "@walletconnect/types": 2.16.1 + "@walletconnect/utils": 2.16.1 events: 3.3.0 - checksum: d8516d5bc7f554962651d59af36c13716da35216e78a92b4ab2632d6c2e65dccc9fda83e5ef8ceaab3195c2436cdd038ff7ed1176b25c57142f823735a5f987c + checksum: e25808e2fbfc01cff47391281e7cb050927cde2e83f5eec640e5be52c8adc9ee50bd70447a2e72defc9cad3d3e3009ef48ac7d1b694dafa5d6a62e046863b58a languageName: node linkType: hard @@ -8869,9 +8910,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/types@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/types@npm:2.13.0" +"@walletconnect/types@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/types@npm:2.16.1" dependencies: "@walletconnect/events": 1.0.1 "@walletconnect/heartbeat": 1.2.2 @@ -8879,46 +8920,48 @@ __metadata: "@walletconnect/keyvaluestorage": 1.1.1 "@walletconnect/logger": 2.1.2 events: 3.3.0 - checksum: 868e12449026154c5a8945359ab03c2f2dd7dd329e631fea721e8399928823b93585013784253d787daf184adb76de6bccd76525679b4c87fd830300c70275d4 + checksum: 9ea47bfb0d5db8f0e440e040d55b05b4932aa3f56e976d42290e831c39d4bc5f2d4cd400f64ca86d8a6a195e34d6370f8db878f7b339ff7cac60a12bbfd9e445 languageName: node linkType: hard -"@walletconnect/universal-provider@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/universal-provider@npm:2.13.0" +"@walletconnect/universal-provider@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/universal-provider@npm:2.16.1" dependencies: "@walletconnect/jsonrpc-http-connection": 1.0.8 "@walletconnect/jsonrpc-provider": 1.0.14 "@walletconnect/jsonrpc-types": 1.0.4 "@walletconnect/jsonrpc-utils": 1.0.8 "@walletconnect/logger": 2.1.2 - "@walletconnect/sign-client": 2.13.0 - "@walletconnect/types": 2.13.0 - "@walletconnect/utils": 2.13.0 + "@walletconnect/sign-client": 2.16.1 + "@walletconnect/types": 2.16.1 + "@walletconnect/utils": 2.16.1 events: 3.3.0 - checksum: 3eb26d07bebbebe67e7f1e666d7b37cbdb6513a807262b9fd9026e8340238bc715b80f99d81127939aa53ff9f9027f903d9828e649e9f6c3c1e536c557b0840d + checksum: 2852ff1b1b06628bf08015ade517f73b743f11873f56e4358063668fd13c290adc648274cc965b34789f2c91879ec338135b42c01e466a2ef76e82ccec74400e languageName: node linkType: hard -"@walletconnect/utils@npm:2.13.0": - version: 2.13.0 - resolution: "@walletconnect/utils@npm:2.13.0" +"@walletconnect/utils@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/utils@npm:2.16.1" dependencies: "@stablelib/chacha20poly1305": 1.0.1 "@stablelib/hkdf": 1.0.1 "@stablelib/random": 1.0.2 "@stablelib/sha256": 1.0.1 "@stablelib/x25519": 1.0.3 - "@walletconnect/relay-api": 1.0.10 + "@walletconnect/relay-api": 1.0.11 + "@walletconnect/relay-auth": 1.0.4 "@walletconnect/safe-json": 1.0.2 "@walletconnect/time": 1.0.2 - "@walletconnect/types": 2.13.0 + "@walletconnect/types": 2.16.1 "@walletconnect/window-getters": 1.0.1 "@walletconnect/window-metadata": 1.0.1 detect-browser: 5.3.0 + elliptic: ^6.5.7 query-string: 7.1.3 uint8arrays: 3.1.0 - checksum: ab3c008aa72e573d67f342042e62c04e4aa779bde94f850de53f7bda31a4458665b39af2e33ae6ee6f237aa19f55cef542c75cabbe647218c02075700d2c713f + checksum: 404c5f262e020c208ab30283c1dbe23f7a4876d3d89ebb23dde95ea32deb8ada72886d64151f6a826d21774797fa44feed70d33729661aa0de4b6850b3ace0d5 languageName: node linkType: hard @@ -13016,6 +13059,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.7": + version: 6.5.7 + resolution: "elliptic@npm:6.5.7" + dependencies: + bn.js: ^4.11.9 + brorand: ^1.1.0 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.1 + inherits: ^2.0.4 + minimalistic-assert: ^1.0.1 + minimalistic-crypto-utils: ^1.0.1 + checksum: af0ffddffdbc2fea4eeec74388cd73e62ed5a0eac6711568fb28071566319785df529c968b0bf1250ba4bc628e074b2d64c54a633e034aa6f0c6b152ceb49ab8 + languageName: node + linkType: hard + "emittery@npm:0.10.0": version: 0.10.0 resolution: "emittery@npm:0.10.0" @@ -16836,16 +16894,6 @@ __metadata: languageName: node linkType: hard -"isomorphic-unfetch@npm:3.1.0": - version: 3.1.0 - resolution: "isomorphic-unfetch@npm:3.1.0" - dependencies: - node-fetch: ^2.6.1 - unfetch: ^4.2.0 - checksum: 82b92fe4ec2823a81ab0fc0d11bd94d710e6f9a940d56b3cba31896d4345ec9ffc7949f4ff31ebcae84f6b95f7ebf3474c4c7452b834eb4078ea3f2c37e459c5 - languageName: node - linkType: hard - "isows@npm:1.0.3": version: 1.0.3 resolution: "isows@npm:1.0.3" @@ -18096,8 +18144,8 @@ __metadata: tiny-invariant: ^1.3.1 typescript: ^5.4.5 url-loader: ^4.1.1 - viem: ^2.0.6 - wagmi: 2.11.2 + viem: ^2.21.9 + wagmi: 2.12.12 languageName: unknown linkType: soft @@ -20416,7 +20464,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12": +"node-fetch@npm:^2.6.12": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -26019,13 +26067,6 @@ __metadata: languageName: node linkType: hard -"unfetch@npm:^4.2.0": - version: 4.2.0 - resolution: "unfetch@npm:4.2.0" - checksum: 6a4b2557e1d921eaa80c4425ce27a404945ec26491ed06e62598f333996a91a44c7908cb26dc7c2746d735762b13276cf4aa41829b4c8f438dde63add3045d7a - languageName: node - linkType: hard - "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -26690,12 +26731,34 @@ __metadata: languageName: node linkType: hard -"wagmi@npm:2.11.2": - version: 2.11.2 - resolution: "wagmi@npm:2.11.2" +"viem@npm:^2.21.9": + version: 2.21.9 + resolution: "viem@npm:2.21.9" + dependencies: + "@adraffy/ens-normalize": 1.10.0 + "@noble/curves": 1.4.0 + "@noble/hashes": 1.4.0 + "@scure/bip32": 1.4.0 + "@scure/bip39": 1.4.0 + abitype: 1.0.5 + isows: 1.0.4 + webauthn-p256: 0.0.5 + ws: 8.17.1 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: de6b9789a86cd217ce62833db80bcf3b5a6fbca3a8ef29d74083a5822c8a9c5fabcfecd050a5c5f0aa476eac02164b57a7aef3334030f93b649d4b52040f9332 + languageName: node + linkType: hard + +"wagmi@npm:2.12.12": + version: 2.12.12 + resolution: "wagmi@npm:2.12.12" dependencies: - "@wagmi/connectors": 5.0.26 - "@wagmi/core": 2.12.2 + "@wagmi/connectors": 5.1.11 + "@wagmi/core": 2.13.5 use-sync-external-store: 1.2.0 peerDependencies: "@tanstack/react-query": ">=5.0.0" @@ -26705,7 +26768,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 5fb5d9434261de1760dc0ba5798627e993f1e80f3ec64c351b42ee7b4f0230c6391194f2ba91dca9ed2452dfb8628215dfdbffda73238bef3cbc65c500f8c964 + checksum: b4d2f8ba66f7ff6228400c0759df0f0f0f5b282081af7b22261c5468ef0eaf676427d1319c6bd7efe14ab5c1fefcfcdf8d2ec3cc1bc68e0f23454ba8c1b85c31 languageName: node linkType: hard @@ -26777,6 +26840,16 @@ __metadata: languageName: node linkType: hard +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" + dependencies: + "@noble/curves": ^1.4.0 + "@noble/hashes": ^1.4.0 + checksum: 2837188d1e6d947c87c5728374fb6aec96387cb766f78e7a04d5903774264feb278d68a639748f09997f591e5278796c662bb5c4e8b8286b0f22254694023584 + languageName: node + linkType: hard + "webextension-polyfill@npm:>=0.10.0 <1.0": version: 0.12.0 resolution: "webextension-polyfill@npm:0.12.0" From df7ce652753a5fb4da02ecdebbe973ee070224e8 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 18 Sep 2024 20:21:50 +0700 Subject: [PATCH 04/28] fix: method name --- packages/sdk/src/l2/l2.ts | 2 +- playground/demo/l2/wrap-l2.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/l2/l2.ts b/packages/sdk/src/l2/l2.ts index a8e1cca8..174ee5a7 100644 --- a/packages/sdk/src/l2/l2.ts +++ b/packages/sdk/src/l2/l2.ts @@ -71,7 +71,7 @@ export class LidoSDKL2 extends LidoSDKModule { @Logger('Call:') @ErrorHandler() - public async approveWsethForWrap( + public async approveWstethForWrap( props: WrapProps, ): Promise { const stethAddress = await this.contractAddress(); diff --git a/playground/demo/l2/wrap-l2.tsx b/playground/demo/l2/wrap-l2.tsx index 3a0eb586..cae3862a 100644 --- a/playground/demo/l2/wrap-l2.tsx +++ b/playground/demo/l2/wrap-l2.tsx @@ -39,7 +39,7 @@ export const WrapL2Demo = () => { walletAction title="Approve wstETH For Wrap to stETH" action={() => - l2.approveWsethForWrap({ + l2.approveWstethForWrap({ value: wstethValue ?? ZERO, callback: transactionToast, From 6a188bf2aebe471e5484f615d2596978efb16e5b Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 18 Sep 2024 20:33:33 +0700 Subject: [PATCH 05/28] fix: build --- examples/erlang-bridge/package.json | 2 +- examples/rewards/package.json | 2 +- packages/lido-pulse/package.json | 2 +- yarn.lock | 92 ++--------------------------- 4 files changed, 8 insertions(+), 90 deletions(-) diff --git a/examples/erlang-bridge/package.json b/examples/erlang-bridge/package.json index 643e58f6..215804bc 100644 --- a/examples/erlang-bridge/package.json +++ b/examples/erlang-bridge/package.json @@ -9,6 +9,6 @@ }, "dependencies": { "@lidofinance/lido-ethereum-sdk": "workspace:*", - "viem": "^2.0.6" + "viem": "^2.21.9" } } diff --git a/examples/rewards/package.json b/examples/rewards/package.json index cc2d9170..59a39c71 100644 --- a/examples/rewards/package.json +++ b/examples/rewards/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@lidofinance/lido-ethereum-sdk": "workspace:*", - "viem": "^2.0.6" + "viem": "^2.21.9" }, "devDependencies": { "rimraf": "^5.0.5", diff --git a/packages/lido-pulse/package.json b/packages/lido-pulse/package.json index dd752956..3dc086ec 100644 --- a/packages/lido-pulse/package.json +++ b/packages/lido-pulse/package.json @@ -53,7 +53,7 @@ "fastify": "^4.28.1", "fastify-plugin": "^4.5.1", "reflect-metadata": "^0.2.2", - "viem": "^2.0.6" + "viem": "^2.21.9" }, "devDependencies": { "@types/node": "^20.14.10", diff --git a/yarn.lock b/yarn.lock index f13800c6..7bc0ac39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5118,7 +5118,7 @@ __metadata: rimraf: ^5.0.5 ts-node: ^10.9.2 typescript: 5.1.6 - viem: ^2.0.6 + viem: ^2.21.9 languageName: unknown linkType: soft @@ -5669,15 +5669,6 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": - version: 1.2.0 - resolution: "@noble/curves@npm:1.2.0" - dependencies: - "@noble/hashes": 1.3.2 - checksum: bb798d7a66d8e43789e93bc3c2ddff91a1e19fdb79a99b86cd98f1e5eff0ee2024a2672902c2576ef3577b6f282f3b5c778bebd55761ddbb30e36bf275e83dd0 - languageName: node - linkType: hard - "@noble/curves@npm:1.3.0, @noble/curves@npm:~1.3.0": version: 1.3.0 resolution: "@noble/curves@npm:1.3.0" @@ -5714,14 +5705,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.2": - version: 1.3.2 - resolution: "@noble/hashes@npm:1.3.2" - checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 - languageName: node - linkType: hard - -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b @@ -6706,7 +6690,7 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2, @scure/base@npm:~1.1.4": +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.4": version: 1.1.5 resolution: "@scure/base@npm:1.1.5" checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 @@ -6727,17 +6711,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.3.2": - version: 1.3.2 - resolution: "@scure/bip32@npm:1.3.2" - dependencies: - "@noble/curves": ~1.2.0 - "@noble/hashes": ~1.3.2 - "@scure/base": ~1.1.2 - checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 - languageName: node - linkType: hard - "@scure/bip32@npm:1.3.3": version: 1.3.3 resolution: "@scure/bip32@npm:1.3.3" @@ -6760,16 +6733,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.2.1": - version: 1.2.1 - resolution: "@scure/bip39@npm:1.2.1" - dependencies: - "@noble/hashes": ~1.3.0 - "@scure/base": ~1.1.0 - checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa - languageName: node - linkType: hard - "@scure/bip39@npm:1.2.2": version: 1.2.2 resolution: "@scure/bip39@npm:1.2.2" @@ -9191,21 +9154,6 @@ __metadata: languageName: node linkType: hard -"abitype@npm:1.0.0": - version: 1.0.0 - resolution: "abitype@npm:1.0.0" - peerDependencies: - typescript: ">=5.0.4" - zod: ^3 >=3.22.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - checksum: ea2c0548c3ba58c37a6de7483d63389074da498e63d803b742bbe94eb4eaa1f51a35d000c424058b2583aef56698cf07c696eb3bc4dd0303bc20c6f0826a241a - languageName: node - linkType: hard - "abitype@npm:1.0.5": version: 1.0.5 resolution: "abitype@npm:1.0.5" @@ -13242,7 +13190,7 @@ __metadata: resolution: "erlang-bridge-examples@workspace:examples/erlang-bridge" dependencies: "@lidofinance/lido-ethereum-sdk": "workspace:*" - viem: ^2.0.6 + viem: ^2.21.9 languageName: unknown linkType: soft @@ -16894,15 +16842,6 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.3": - version: 1.0.3 - resolution: "isows@npm:1.0.3" - peerDependencies: - ws: "*" - checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 - languageName: node - linkType: hard - "isows@npm:1.0.4": version: 1.0.4 resolution: "isows@npm:1.0.4" @@ -23723,7 +23662,7 @@ __metadata: "@lidofinance/lido-ethereum-sdk": "workspace:*" rimraf: ^5.0.5 typescript: 5.1.6 - viem: ^2.0.6 + viem: ^2.21.9 languageName: unknown linkType: soft @@ -26689,27 +26628,6 @@ __metadata: languageName: node linkType: hard -"viem@npm:^2.0.6": - version: 2.7.9 - resolution: "viem@npm:2.7.9" - dependencies: - "@adraffy/ens-normalize": 1.10.0 - "@noble/curves": 1.2.0 - "@noble/hashes": 1.3.2 - "@scure/bip32": 1.3.2 - "@scure/bip39": 1.2.1 - abitype: 1.0.0 - isows: 1.0.3 - ws: 8.13.0 - peerDependencies: - typescript: ">=5.0.4" - peerDependenciesMeta: - typescript: - optional: true - checksum: 71ff08ca18506a7217f1f87f44ea84ba2dcdb81f7d5a35670701aefbb05512892f4e32c80c9ca9584db119749d53576f37802290da6234fb1196d4c0c28beed5 - languageName: node - linkType: hard - "viem@npm:^2.1.1": version: 2.17.11 resolution: "viem@npm:2.17.11" From fdce1a07508f8608117a653c7233e713fae49da7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:25:07 +0000 Subject: [PATCH 06/28] build(deps): bump find-my-way from 8.2.0 to 8.2.2 Bumps [find-my-way](https://github.com/delvedor/find-my-way) from 8.2.0 to 8.2.2. - [Release notes](https://github.com/delvedor/find-my-way/releases) - [Commits](https://github.com/delvedor/find-my-way/compare/v8.2.0...v8.2.2) --- updated-dependencies: - dependency-name: find-my-way dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b9d50ced..aebcc18e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14448,13 +14448,13 @@ __metadata: linkType: hard "find-my-way@npm:^8.0.0": - version: 8.2.0 - resolution: "find-my-way@npm:8.2.0" + version: 8.2.2 + resolution: "find-my-way@npm:8.2.2" dependencies: fast-deep-equal: ^3.1.3 fast-querystring: ^1.0.0 safe-regex2: ^3.1.0 - checksum: 4f59fe17a1431511ec172403da0d1ac05bf9efebfdd4c7149b658d748b2570b63d798847e08ceea00f57543611fdb64ba3793dfc67a9ed7b5bfa0d77c8693eb5 + checksum: bba4feafece3ef012180e561d904ead38e1f2e089bd97dbcb2c628d38cb869918626c673b08b698d22ef46265a886aa535a650574801ecab9c8eb8db5437736a languageName: node linkType: hard From 983bae4643368a7171178f223249300be8b792c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:41:11 +0000 Subject: [PATCH 07/28] build(deps): bump express from 4.19.2 to 4.21.0 Bumps [express](https://github.com/expressjs/express) from 4.19.2 to 4.21.0. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md) - [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 107 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/yarn.lock b/yarn.lock index b9d50ced..59fbb08f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10239,9 +10239,9 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.2": - version: 1.20.2 - resolution: "body-parser@npm:1.20.2" +"body-parser@npm:1.20.3": + version: 1.20.3 + resolution: "body-parser@npm:1.20.3" dependencies: bytes: 3.1.2 content-type: ~1.0.5 @@ -10251,11 +10251,11 @@ __metadata: http-errors: 2.0.0 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.11.0 + qs: 6.13.0 raw-body: 2.5.2 type-is: ~1.6.18 unpipe: 1.0.0 - checksum: 14d37ec638ab5c93f6099ecaed7f28f890d222c650c69306872e00b9efa081ff6c596cd9afb9930656aae4d6c4e1c17537bea12bb73c87a217cb3cfea8896737 + checksum: 1a35c59a6be8d852b00946330141c4f142c6af0f970faa87f10ad74f1ee7118078056706a05ae3093c54dabca9cd3770fa62a170a85801da1a4324f04381167d languageName: node linkType: hard @@ -13079,6 +13079,13 @@ __metadata: languageName: node linkType: hard +"encodeurl@npm:~2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -14065,41 +14072,41 @@ __metadata: linkType: hard "express@npm:^4.17.3": - version: 4.19.2 - resolution: "express@npm:4.19.2" + version: 4.21.0 + resolution: "express@npm:4.21.0" dependencies: accepts: ~1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.2 + body-parser: 1.20.3 content-disposition: 0.5.4 content-type: ~1.0.4 cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 - encodeurl: ~1.0.2 + encodeurl: ~2.0.0 escape-html: ~1.0.3 etag: ~1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.3.1 fresh: 0.5.2 http-errors: 2.0.0 - merge-descriptors: 1.0.1 + merge-descriptors: 1.0.3 methods: ~1.1.2 on-finished: 2.4.1 parseurl: ~1.3.3 - path-to-regexp: 0.1.7 + path-to-regexp: 0.1.10 proxy-addr: ~2.0.7 - qs: 6.11.0 + qs: 6.13.0 range-parser: ~1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.19.0 + serve-static: 1.16.2 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: ~1.6.18 utils-merge: 1.0.1 vary: ~1.1.2 - checksum: 212dbd6c2c222a96a61bc927639c95970a53b06257080bb9e2838adb3bffdb966856551fdad1ab5dd654a217c35db94f987d0aa88d48fb04d306340f5f34dca5 + checksum: 1c5212993f665809c249bf00ab550b989d1365a5b9171cdfaa26d93ee2ef10cd8add520861ec8d5da74b3194d8374e1d9d53e85ef69b89fd9c4196b87045a5d4 languageName: node linkType: hard @@ -14422,18 +14429,18 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.2.0": - version: 1.2.0 - resolution: "finalhandler@npm:1.2.0" +"finalhandler@npm:1.3.1": + version: 1.3.1 + resolution: "finalhandler@npm:1.3.1" dependencies: debug: 2.6.9 - encodeurl: ~1.0.2 + encodeurl: ~2.0.0 escape-html: ~1.0.3 on-finished: 2.4.1 parseurl: ~1.3.3 statuses: 2.0.1 unpipe: ~1.0.0 - checksum: 92effbfd32e22a7dff2994acedbd9bcc3aa646a3e919ea6a53238090e87097f8ef07cced90aa2cc421abdf993aefbdd5b00104d55c7c5479a8d00ed105b45716 + checksum: a8c58cd97c9cd47679a870f6833a7b417043f5a288cd6af6d0f49b476c874a506100303a128b6d3b654c3d74fa4ff2ffed68a48a27e8630cda5c918f2977dcf4 languageName: node linkType: hard @@ -19018,10 +19025,10 @@ __metadata: languageName: node linkType: hard -"merge-descriptors@npm:1.0.1": - version: 1.0.1 - resolution: "merge-descriptors@npm:1.0.1" - checksum: 5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 +"merge-descriptors@npm:1.0.3": + version: 1.0.3 + resolution: "merge-descriptors@npm:1.0.3" + checksum: 52117adbe0313d5defa771c9993fe081e2d2df9b840597e966aadafde04ae8d0e3da46bac7ca4efc37d4d2b839436582659cd49c6a43eacb3fe3050896a105d1 languageName: node linkType: hard @@ -21496,10 +21503,10 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:0.1.7": - version: 0.1.7 - resolution: "path-to-regexp@npm:0.1.7" - checksum: 69a14ea24db543e8b0f4353305c5eac6907917031340e5a8b37df688e52accd09e3cebfe1660b70d76b6bd89152f52183f28c74813dbf454ba1a01c82a38abce +"path-to-regexp@npm:0.1.10": + version: 0.1.10 + resolution: "path-to-regexp@npm:0.1.10" + checksum: ab7a3b7a0b914476d44030340b0a65d69851af2a0f33427df1476100ccb87d409c39e2182837a96b98fb38c4ef2ba6b87bdad62bb70a2c153876b8061760583c languageName: node linkType: hard @@ -22584,12 +22591,12 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.11.0": - version: 6.11.0 - resolution: "qs@npm:6.11.0" +"qs@npm:6.13.0": + version: 6.13.0 + resolution: "qs@npm:6.13.0" dependencies: - side-channel: ^1.0.4 - checksum: 6e1f29dd5385f7488ec74ac7b6c92f4d09a90408882d0c208414a34dd33badc1a621019d4c799a3df15ab9b1d0292f97c1dd71dc7c045e69f81a8064e5af7297 + side-channel: ^1.0.6 + checksum: e9404dc0fc2849245107108ce9ec2766cde3be1b271de0bf1021d049dc5b98d1a2901e67b431ac5509f865420a7ed80b7acb3980099fe1c118a1c5d2e1432ad8 languageName: node linkType: hard @@ -24077,9 +24084,9 @@ __metadata: languageName: node linkType: hard -"send@npm:0.18.0": - version: 0.18.0 - resolution: "send@npm:0.18.0" +"send@npm:0.19.0": + version: 0.19.0 + resolution: "send@npm:0.19.0" dependencies: debug: 2.6.9 depd: 2.0.0 @@ -24094,7 +24101,7 @@ __metadata: on-finished: 2.4.1 range-parser: ~1.2.1 statuses: 2.0.1 - checksum: 74fc07ebb58566b87b078ec63e5a3e41ecd987e4272ba67b7467e86c6ad51bc6b0b0154133b6d8b08a2ddda360464f71382f7ef864700f34844a76c8027817a8 + checksum: 5ae11bd900c1c2575525e2aa622e856804e2f96a09281ec1e39610d089f53aa69e13fd8db84b52f001d0318cf4bb0b3b904ad532fc4c0014eb90d32db0cff55f languageName: node linkType: hard @@ -24138,15 +24145,15 @@ __metadata: languageName: node linkType: hard -"serve-static@npm:1.15.0": - version: 1.15.0 - resolution: "serve-static@npm:1.15.0" +"serve-static@npm:1.16.2": + version: 1.16.2 + resolution: "serve-static@npm:1.16.2" dependencies: - encodeurl: ~1.0.2 + encodeurl: ~2.0.0 escape-html: ~1.0.3 parseurl: ~1.3.3 - send: 0.18.0 - checksum: af57fc13be40d90a12562e98c0b7855cf6e8bd4c107fe9a45c212bf023058d54a1871b1c89511c3958f70626fff47faeb795f5d83f8cf88514dbaeb2b724464d + send: 0.19.0 + checksum: dffc52feb4cc5c68e66d0c7f3c1824d4e989f71050aefc9bd5f822a42c54c9b814f595fc5f2b717f4c7cc05396145f3e90422af31186a93f76cf15f707019759 languageName: node linkType: hard @@ -24286,6 +24293,18 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.0.6": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: ^1.0.7 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.4 + object-inspect: ^1.13.1 + checksum: bfc1afc1827d712271453e91b7cd3878ac0efd767495fd4e594c4c2afaa7963b7b510e249572bfd54b0527e66e4a12b61b80c061389e129755f34c493aad9b97 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" From 4490f62f2886fb7b2293cb7ad733474d72de915f Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 30 Sep 2024 15:57:44 +0700 Subject: [PATCH 08/28] test: base l2 test setup --- packages/sdk/src/common/constants.ts | 30 +++-- packages/sdk/src/core/core.ts | 4 +- packages/sdk/src/l2/__test__/l2.test.ts | 100 ++++++++++++++++ packages/sdk/src/l2/l2.ts | 14 ++- packages/sdk/src/rewards/rewards.ts | 8 +- .../sdk/src/withdraw/withdraw-waiting-time.ts | 2 +- packages/sdk/tests/global-setup.cjs | 55 +++++---- packages/sdk/tests/global-teardown.cjs | 5 + .../tests/utils/expect/expect-erc20-wallet.ts | 15 ++- .../sdk/tests/utils/expect/expect-erc20.ts | 30 +++-- packages/sdk/tests/utils/fixtures/use-l2.ts | 110 ++++++++++++++++++ .../sdk/tests/utils/fixtures/use-test-envs.ts | 2 + 12 files changed, 321 insertions(+), 54 deletions(-) create mode 100644 packages/sdk/src/l2/__test__/l2.test.ts create mode 100644 packages/sdk/tests/utils/fixtures/use-l2.ts diff --git a/packages/sdk/src/common/constants.ts b/packages/sdk/src/common/constants.ts index d96e60f6..c5dac6c2 100644 --- a/packages/sdk/src/common/constants.ts +++ b/packages/sdk/src/common/constants.ts @@ -5,6 +5,7 @@ import { holesky, sepolia, optimismSepolia, + optimism, } from 'viem/chains'; export enum CHAINS { @@ -12,6 +13,7 @@ export enum CHAINS { Mainnet = 1, Holesky = 17000, Sepolia = 11155111, + Optimism = 10, OptimismSepolia = 11155420, } @@ -30,33 +32,28 @@ export const GAS_TRANSACTION_RATIO_PRECISION = 10 ** 7; export const ESTIMATE_ACCOUNT = '0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1'; export const LIDO_LOCATOR_BY_CHAIN: { - [key in CHAINS]: Address | null; + [key in CHAINS]?: Address; } = { [CHAINS.Mainnet]: '0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb', [CHAINS.Goerli]: '0x1eDf09b5023DC86737b59dE68a8130De878984f5', [CHAINS.Holesky]: '0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8', [CHAINS.Sepolia]: '0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7', - [CHAINS.OptimismSepolia]: null, }; export const SUBRGRAPH_ID_BY_CHAIN: { - [key in CHAINS]: string | null; + [key in CHAINS]?: string; } = { [CHAINS.Mainnet]: 'Sxx812XgeKyzQPaBpR5YZWmGV5fZuBaPdh7DFhzSwiQ', [CHAINS.Goerli]: 'QmeDfGTuNbSoZ71zi3Ch4WNRbzALfiFPnJMYUFPinLiFNa', - [CHAINS.Holesky]: null, - [CHAINS.Sepolia]: null, - [CHAINS.OptimismSepolia]: null, }; export const EARLIEST_TOKEN_REBASED_EVENT: { - [key in CHAINS]: bigint; + [key in CHAINS]?: bigint; } = { [CHAINS.Mainnet]: 17272708n, [CHAINS.Goerli]: 8712039n, [CHAINS.Holesky]: 52174n, [CHAINS.Sepolia]: 5434668n, - [CHAINS.OptimismSepolia]: 0n, } as const; export const LIDO_TOKENS = { @@ -89,15 +86,17 @@ export const enum LIDO_L2_CONTRACT_NAMES { steth = 'steth', } -export const LIDO_L2_CONTRACT_ADDRESSES: Record< - number, - Record -> = { +export const LIDO_L2_CONTRACT_ADDRESSES: { + [key in CHAINS]?: { [key2 in LIDO_L2_CONTRACT_NAMES]?: Address }; +} = { [CHAINS.OptimismSepolia]: { wsteth: '0x24B47cd3A74f1799b32B2de11073764Cb1bb318B', steth: '0xf49d208b5c7b10415c7beafe9e656f2df9edfe3b', }, -} as const; + [CHAINS.Optimism]: { + wsteth: '0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb', + }, +}; export const CONTRACTS_BY_TOKENS = { [LIDO_TOKENS.steth]: LIDO_CONTRACT_NAMES.lido, @@ -136,13 +135,12 @@ export const VIEM_CHAINS: { [key in CHAINS]: Chain } = { [CHAINS.Goerli]: goerli, [CHAINS.Holesky]: holesky, [CHAINS.Sepolia]: sepolia, + [CHAINS.Optimism]: optimism, [CHAINS.OptimismSepolia]: optimismSepolia, }; -export const WQ_API_URLS: { [key in CHAINS]: string | null } = { +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', - [CHAINS.Sepolia]: null, - [CHAINS.OptimismSepolia]: null, }; diff --git a/packages/sdk/src/core/core.ts b/packages/sdk/src/core/core.ts index cb73b325..8aeb0b18 100644 --- a/packages/sdk/src/core/core.ts +++ b/packages/sdk/src/core/core.ts @@ -390,7 +390,7 @@ export default class LidoSDKCore extends LidoSDKCacheable { @Logger('Utils:') @Cache(30 * 60 * 1000, ['chain.id']) public getL2ContractAddress(contract: LIDO_L2_CONTRACT_NAMES): Address { - const chainConfig = LIDO_L2_CONTRACT_ADDRESSES[this.chain.id]; + const chainConfig = LIDO_L2_CONTRACT_ADDRESSES[this.chain.id as CHAINS]; invariant( chainConfig, `Lido L2 contracts are not supported for ${this.chain.name}(${this.chain.id})`, @@ -408,7 +408,7 @@ export default class LidoSDKCore extends LidoSDKCacheable { @Logger('Utils:') @Cache(30 * 60 * 1000, ['chain.id']) public getSubgraphId(): string | null { - const id = SUBRGRAPH_ID_BY_CHAIN[this.chainId]; + const id = SUBRGRAPH_ID_BY_CHAIN[this.chainId] ?? null; return id; } diff --git a/packages/sdk/src/l2/__test__/l2.test.ts b/packages/sdk/src/l2/__test__/l2.test.ts new file mode 100644 index 00000000..0e21a598 --- /dev/null +++ b/packages/sdk/src/l2/__test__/l2.test.ts @@ -0,0 +1,100 @@ +import { beforeAll, describe, test } from '@jest/globals'; +import { expectSDKModule } from '../../../tests/utils/expect/expect-sdk-module.js'; +import { + useL2, + useL2Rpc, + useTestL2RpcProvider, +} from '../../../tests/utils/fixtures/use-l2.js'; +import { LidoSDKL2 } from '../l2.js'; +import { LidoSDKL2Steth, LidoSDKL2Wsteth } from '../tokens.js'; +import { expectERC20 } from '../../../tests/utils/expect/expect-erc20.js'; +import { LIDO_L2_CONTRACT_NAMES } from '../../common/constants.js'; +import { expectERC20Wallet } from '../../../tests/utils/expect/expect-erc20-wallet.js'; +import { useAccount } from '../../../tests/utils/fixtures/use-wallet-client.js'; +import { getContract } from 'viem'; +import { bridgedWstethAbi } from '../abi/brigedWsteth.js'; + +describe('LidoSDKL2', () => { + test('is correct module', () => { + expectSDKModule(LidoSDKL2); + }); +}); + +describe('LidoSDKL2Wsteth & LidoSDKL2Steth', () => { + const l2 = useL2(); + const l2Rpc = useL2Rpc(); + const account = useAccount(); + const { testClient } = useTestL2RpcProvider(); + + beforeAll(async () => { + const wstethAddress = await l2.wsteth.contractAddress(); + + const wstethImpersonated = getContract({ + abi: bridgedWstethAbi, + address: wstethAddress, + client: testClient, + }); + + const bridge = await wstethImpersonated.read.bridge(); + + await testClient.setBalance({ + address: account.address, + value: 100000000000000n, + }); + + await testClient.setBalance({ + address: bridge, + value: 100000000000000n, + }); + + await testClient.request({ + method: 'evm_addAccount' as any, + params: [bridge, 'pass'], + }); + + await testClient.request({ + method: 'personal_unlockAccount' as any, + params: [bridge, 'pass'], + }); + + await wstethImpersonated.write.bridgeMint([account.address, 2000n], { + account: bridge, + chain: testClient.chain, + }); + + await l2.approveWstethForWrap({ value: 1000n, account }); + await l2.wrapWstethToSteth({ value: 1000n, account }); + }); + + // wstETH erc20 tests + expectERC20({ + contractName: LIDO_L2_CONTRACT_NAMES.wsteth, + constructedWithWeb3Core: l2.wsteth, + isL2: true, + ModulePrototype: LidoSDKL2Wsteth, + constructedWithRpcCore: l2Rpc.wsteth, + }); + + expectERC20Wallet({ + contractName: LIDO_L2_CONTRACT_NAMES.wsteth, + constructedWithWeb3Core: l2.wsteth, + isL2: true, + constructedWithRpcCore: l2Rpc.wsteth, + }); + + // stETH erc20 tests + expectERC20({ + ModulePrototype: LidoSDKL2Steth, + contractName: LIDO_L2_CONTRACT_NAMES.steth, + constructedWithWeb3Core: l2.steth, + isL2: true, + constructedWithRpcCore: l2Rpc.steth, + }); + + expectERC20Wallet({ + contractName: LIDO_L2_CONTRACT_NAMES.steth, + constructedWithWeb3Core: l2.steth, + isL2: true, + constructedWithRpcCore: l2Rpc.steth, + }); +}); diff --git a/packages/sdk/src/l2/l2.ts b/packages/sdk/src/l2/l2.ts index 174ee5a7..7d454045 100644 --- a/packages/sdk/src/l2/l2.ts +++ b/packages/sdk/src/l2/l2.ts @@ -16,7 +16,12 @@ import { type TransactionReceipt, } from 'viem'; -import { LIDO_L2_CONTRACT_NAMES, NOOP } from '../common/constants.js'; +import { + CHAINS, + LIDO_L2_CONTRACT_ADDRESSES, + LIDO_L2_CONTRACT_NAMES, + NOOP, +} from '../common/constants.js'; import { Cache, Logger, ErrorHandler } from '../common/decorators/index.js'; import { rebasableL2StethAbi } from './abi/rebasableL2Steth.js'; @@ -36,6 +41,13 @@ export class LidoSDKL2 extends LidoSDKModule { getAbiItem({ abi: rebasableL2StethAbi, name: 'Transfer' }), ); + public static isContractAvailableOn( + contract: LIDO_L2_CONTRACT_NAMES, + chain: CHAINS, + ) { + return !!LIDO_L2_CONTRACT_ADDRESSES[chain]?.[contract]; + } + public readonly wsteth: LidoSDKL2Wsteth; public readonly steth: LidoSDKL2Steth; diff --git a/packages/sdk/src/rewards/rewards.ts b/packages/sdk/src/rewards/rewards.ts index ab76db91..83baeda1 100644 --- a/packages/sdk/src/rewards/rewards.ts +++ b/packages/sdk/src/rewards/rewards.ts @@ -66,7 +66,13 @@ export class LidoSDKRewards extends LidoSDKModule { @Logger('Contracts:') @Cache(30 * 60 * 1000, ['core.chain.id']) private earliestRebaseEventBlock(): bigint { - return EARLIEST_TOKEN_REBASED_EVENT[this.core.chainId]; + const block = EARLIEST_TOKEN_REBASED_EVENT[this.core.chainId]; + invariant( + block, + `No rebase event for chain:${this.core.chainId}`, + ERROR_CODE.NOT_SUPPORTED, + ); + return block; } @Logger('Contracts:') diff --git a/packages/sdk/src/withdraw/withdraw-waiting-time.ts b/packages/sdk/src/withdraw/withdraw-waiting-time.ts index 4727674f..7ef8a829 100644 --- a/packages/sdk/src/withdraw/withdraw-waiting-time.ts +++ b/packages/sdk/src/withdraw/withdraw-waiting-time.ts @@ -97,7 +97,7 @@ export class LidoSDKWithdrawWaitingTime extends BusModule { const baseUrl = getCustomApiUrl && typeof getCustomApiUrl === 'function' - ? getCustomApiUrl(defaultUrl, this.bus.core.chainId) + ? getCustomApiUrl(defaultUrl ?? null, this.bus.core.chainId) : defaultUrl; if (!baseUrl) { diff --git a/packages/sdk/tests/global-setup.cjs b/packages/sdk/tests/global-setup.cjs index 3ce86ca9..fc42f123 100644 --- a/packages/sdk/tests/global-setup.cjs +++ b/packages/sdk/tests/global-setup.cjs @@ -2,49 +2,64 @@ const path = require('path'); const dotenv = require('dotenv'); const ganache = require('ganache'); -module.exports = async function () { - dotenv.config({ - path: path.resolve(process.cwd(), '.env'), - }); - - const rpcUrl = process.env.TEST_RPC_URL; - const chainId = Number(process.env.TEST_CHAIN_ID); - +const setupGanacheProvider = async (chainId, rpcUrl) => { const ganacheProvider = ganache.provider({ fork: { url: rpcUrl }, logging: { quiet: true }, chain: { chainId, asyncRequestProcessing: true }, }); - - console.debug('\nInitializing ganache provider...'); + console.debug(`\n[${chainId}]Initializing ganache provider...`); await ganacheProvider.initialize(); - console.debug('Initialized ganache provider OK'); + console.debug(`[${chainId}]Initialized ganache provider OK`); - console.debug('Testing direct RPC provider...'); + console.debug(`[${chainId}]Testing direct RPC provider...`); const { result } = await fetch(rpcUrl, { method: 'POST', body: JSON.stringify({ method: 'eth_chainId', params: [], + id: 1, + jsonrpc: '2.0', }), headers: { 'Content-Type': 'application/json', }, }).then((response) => response.json()); - if (Number(result) !== chainId) { - throw new Error(`Invalid direct RPC provider response: ${result}`); + if (parseInt(result, 16) !== chainId) { + throw new Error( + `[${chainId}]Invalid direct RPC provider response: ${result}`, + ); } - console.debug('Direct RPC provider OK'); + console.debug(`[${chainId}]Direct RPC provider OK`); - console.debug('Testing ganache fork RPC provider...'); + console.debug(`[${chainId}]Testing ganache fork RPC provider...`); const testRequest = await ganacheProvider.request({ method: 'eth_chainId', params: [], }); - if (Number(testRequest) !== chainId) { - throw new Error(`Invalid ganache response: ${testRequest}`); + if (parseInt(testRequest, 16) !== chainId) { + throw new Error(`[${chainId}]Invalid ganache response: ${testRequest}`); } - console.debug('Ganache fork RPC provider OK'); + console.debug(`[${chainId}]Ganache fork RPC provider OK`); + return ganacheProvider; +}; + +module.exports = async function () { + dotenv.config({ + path: path.resolve(process.cwd(), '.env'), + }); + + // L1 + const chainId = Number(process.env.TEST_CHAIN_ID); + const rpcUrl = process.env.TEST_RPC_URL; + globalThis.__ganache_provider__ = await setupGanacheProvider(chainId, rpcUrl); + + // L2 + const l2RpcUrl = process.env.TEST_L2_RPC_URL; + const l2ChainId = Number(process.env.TEST_L2_CHAIN_ID); - globalThis.__ganache_provider__ = ganacheProvider; + globalThis.__l2_ganache_provider__ = await setupGanacheProvider( + l2ChainId, + l2RpcUrl, + ); }; diff --git a/packages/sdk/tests/global-teardown.cjs b/packages/sdk/tests/global-teardown.cjs index 2413a5b6..460e8d3c 100644 --- a/packages/sdk/tests/global-teardown.cjs +++ b/packages/sdk/tests/global-teardown.cjs @@ -4,4 +4,9 @@ module.exports = async function () { await globalThis.__ganache_provider__.disconnect(); console.debug('Disconnected ganache provider'); } + if (globalThis.__l2_ganache_provider__) { + console.debug('Disconnecting L2 ganache provider...'); + await globalThis.__l2_ganache_provider__.disconnect(); + console.debug('Disconnected L2 ganache provider'); + } }; diff --git a/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts b/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts index bb6d703a..330893d0 100644 --- a/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts +++ b/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts @@ -3,6 +3,7 @@ import { expect, describe, test, jest } from '@jest/globals'; import { AbstractLidoSDKErc20 } from '../../../src/erc20/erc20.js'; import { LIDO_CONTRACT_NAMES, + LIDO_L2_CONTRACT_NAMES, PERMIT_MESSAGE_TYPES, } from '../../../src/index.js'; import { @@ -51,10 +52,12 @@ export const expectERC20Wallet = ({ contractName, constructedWithRpcCore, constructedWithWeb3Core, + isL2 = false, }: { - contractName: LIDO_CONTRACT_NAMES; + contractName: LIDO_CONTRACT_NAMES | LIDO_L2_CONTRACT_NAMES; constructedWithRpcCore: I; constructedWithWeb3Core: I; + isL2?: boolean; }) => { const token = constructedWithWeb3Core; const tokenRpc = constructedWithRpcCore; @@ -62,11 +65,15 @@ export const expectERC20Wallet = ({ const rpcCore = tokenRpc.core; const getTokenAddress = async () => { - const address = - await constructedWithRpcCore.core.getContractAddress(contractName); + const address = await (isL2 + ? constructedWithRpcCore.core.getL2ContractAddress( + contractName as LIDO_L2_CONTRACT_NAMES, + ) + : constructedWithRpcCore.core.getContractAddress( + contractName as LIDO_CONTRACT_NAMES, + )); return address; }; - const getTokenContract = async () => { const address = await getTokenAddress(); return getContract({ diff --git a/packages/sdk/tests/utils/expect/expect-erc20.ts b/packages/sdk/tests/utils/expect/expect-erc20.ts index ea80600a..20a098f8 100644 --- a/packages/sdk/tests/utils/expect/expect-erc20.ts +++ b/packages/sdk/tests/utils/expect/expect-erc20.ts @@ -1,7 +1,10 @@ import { getContract } from 'viem'; import { expect, describe, test } from '@jest/globals'; import { AbstractLidoSDKErc20 } from '../../../src/erc20/erc20.js'; -import { LIDO_CONTRACT_NAMES } from '../../../src/index.js'; +import { + LIDO_CONTRACT_NAMES, + LIDO_L2_CONTRACT_NAMES, +} from '../../../src/index.js'; import { expectAddress } from '../../../tests/utils/expect/expect-address.js'; import { expectContract } from '../../../tests/utils/expect/expect-contract.js'; import { useAccount } from '../../../tests/utils/fixtures/use-wallet-client.js'; @@ -10,25 +13,34 @@ import { expectBn } from '../../../tests/utils/expect/expect-bn.js'; import { expectSDKModule } from '../../../tests/utils/expect/expect-sdk-module.js'; import { LidoSDKCommonProps } from '../../../src/core/types.js'; +type expectERC20Options = { + contractName: LIDO_CONTRACT_NAMES | LIDO_L2_CONTRACT_NAMES; + constructedWithRpcCore: I; + constructedWithWeb3Core: I; + ModulePrototype: new (props: LidoSDKCommonProps) => I; + isL2?: boolean; +}; + export const expectERC20 = ({ contractName, constructedWithRpcCore, constructedWithWeb3Core, ModulePrototype, -}: { - contractName: LIDO_CONTRACT_NAMES; - constructedWithRpcCore: I; - constructedWithWeb3Core: I; - ModulePrototype: new (props: LidoSDKCommonProps) => I; -}) => { + isL2 = false, +}: expectERC20Options) => { const token = constructedWithWeb3Core; const tokenRpc = constructedWithRpcCore; const web3Core = token.core; const rpcCore = tokenRpc.core; const getTokenAddress = async () => { - const address = - await constructedWithRpcCore.core.getContractAddress(contractName); + const address = await (isL2 + ? constructedWithRpcCore.core.getL2ContractAddress( + contractName as LIDO_L2_CONTRACT_NAMES, + ) + : constructedWithRpcCore.core.getContractAddress( + contractName as LIDO_CONTRACT_NAMES, + )); return address; }; diff --git a/packages/sdk/tests/utils/fixtures/use-l2.ts b/packages/sdk/tests/utils/fixtures/use-l2.ts new file mode 100644 index 00000000..9230d7d9 --- /dev/null +++ b/packages/sdk/tests/utils/fixtures/use-l2.ts @@ -0,0 +1,110 @@ +import type { EthereumProvider } from 'ganache'; +import { + createTestClient, + createWalletClient, + custom, + PrivateKeyAccount, + publicActions, + PublicClient, + TestClient, +} from 'viem'; +import { useTestsEnvs } from './use-test-envs.js'; +import { CHAINS, LidoSDKCore, VIEM_CHAINS } from '../../../src/index.js'; +import { useAccount } from './use-wallet-client.js'; +import { LidoSDKL2 } from '../../../src/l2/l2.js'; + +let cached: { + testClient: TestClient<'ganache'>; + ganacheProvider: EthereumProvider; +} | null = null; + +export const useTestL2RpcProvider = () => { + if (cached) return cached; + const { l2ChainId } = useTestsEnvs(); + + const ganacheProvider = (globalThis as any) + .__l2_ganache_provider__ as EthereumProvider; + + const testClient = createTestClient({ + mode: 'ganache', + transport: custom({ + async request(args) { + if (args.method === 'eth_estimateGas') { + delete args.params[0].gas; + } + return ganacheProvider.request(args); + }, + }), + name: 'testClient', + chain: VIEM_CHAINS[l2ChainId as CHAINS], + }); + + cached = { ganacheProvider, testClient }; + return cached; +}; + +let cachedPublicProvider: PublicClient | null = null; + +export const usePublicL2RpcProvider = () => { + if (cachedPublicProvider) return cachedPublicProvider; + const { testClient } = useTestL2RpcProvider(); + const rpcProvider = testClient.extend(publicActions) as PublicClient; + cachedPublicProvider = rpcProvider; + return rpcProvider; +}; + +export const useL2WalletClient = (_account?: PrivateKeyAccount) => { + const { l2ChainId } = useTestsEnvs(); + const { testClient } = useTestL2RpcProvider(); + const account = _account ?? useAccount(); + + const chain = VIEM_CHAINS[l2ChainId as CHAINS]; + + return createWalletClient({ + account, + chain, + transport: custom({ request: testClient.request }), + }); +}; + +let cachedWeb3Core: LidoSDKCore | null = null; + +export const useL2Web3Core = () => { + if (!cachedWeb3Core) { + const walletClient = useL2WalletClient(); + const { l2ChainId } = useTestsEnvs(); + const rpcProvider = usePublicL2RpcProvider(); + cachedWeb3Core = new LidoSDKCore({ + chainId: l2ChainId, + rpcProvider: rpcProvider, + logMode: 'none', + web3Provider: walletClient, + }); + } + return cachedWeb3Core; +}; + +let cachedRpcCore: LidoSDKCore | null = null; + +export const useL2RpcCore = () => { + if (!cachedRpcCore) { + const { l2ChainId } = useTestsEnvs(); + const rpcProvider = usePublicL2RpcProvider(); + cachedRpcCore = new LidoSDKCore({ + chainId: l2ChainId, + rpcProvider: rpcProvider, + logMode: 'none', + }); + } + return cachedRpcCore; +}; + +export const useL2Rpc = () => { + const rpcCore = useL2RpcCore(); + return new LidoSDKL2({ core: rpcCore }); +}; + +export const useL2 = () => { + const web3Core = useL2Web3Core(); + return new LidoSDKL2({ core: web3Core }); +}; diff --git a/packages/sdk/tests/utils/fixtures/use-test-envs.ts b/packages/sdk/tests/utils/fixtures/use-test-envs.ts index a01827de..4d0b968e 100644 --- a/packages/sdk/tests/utils/fixtures/use-test-envs.ts +++ b/packages/sdk/tests/utils/fixtures/use-test-envs.ts @@ -3,6 +3,8 @@ export const useTestsEnvs = () => { privateKey: process.env.TEST_PRIVATE_KEY as string, rpcUrl: process.env.TEST_RPC_URL as string, chainId: Number(process.env.TEST_CHAIN_ID), + l2RpcUrl: process.env.TEST_L2_RPC_URL as string, + l2ChainId: Number(process.env.TEST_L2_CHAIN_ID), skipSpendingTests: process.env.TEST_SKIP_SPENDING_TESTS == 'true', subgraphUrl: process.env.TEST_SUBGRAPH_URL, }; From 17eef9c45329e3ea20e6bf4a78cdcc80e291c274 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 30 Sep 2024 17:16:03 +0700 Subject: [PATCH 09/28] test: l2 wrap tests --- packages/sdk/src/l2/__test__/l2.test.ts | 233 ++++++++++++++++++++---- 1 file changed, 199 insertions(+), 34 deletions(-) diff --git a/packages/sdk/src/l2/__test__/l2.test.ts b/packages/sdk/src/l2/__test__/l2.test.ts index 0e21a598..c75d9d3f 100644 --- a/packages/sdk/src/l2/__test__/l2.test.ts +++ b/packages/sdk/src/l2/__test__/l2.test.ts @@ -1,4 +1,4 @@ -import { beforeAll, describe, test } from '@jest/globals'; +import { beforeAll, describe, expect, jest, test } from '@jest/globals'; import { expectSDKModule } from '../../../tests/utils/expect/expect-sdk-module.js'; import { useL2, @@ -13,57 +13,209 @@ import { expectERC20Wallet } from '../../../tests/utils/expect/expect-erc20-wall import { useAccount } from '../../../tests/utils/fixtures/use-wallet-client.js'; import { getContract } from 'viem'; import { bridgedWstethAbi } from '../abi/brigedWsteth.js'; +import { expectAddress } from '../../../tests/utils/expect/expect-address.js'; +import { expectContract } from '../../../tests/utils/expect/expect-contract.js'; +import { + expectAlmostEqualBn, + expectNonNegativeBn, +} from '../../../tests/utils/expect/expect-bn.js'; +import { testSpending } from '../../../tests/utils/test-spending.js'; +import { expectTxCallback } from '../../../tests/utils/expect/expect-tx-callback.js'; +import { + expectPopulatedTx, + expectPopulatedTxToRun, +} from '../../../tests/utils/expect/expect-populated-tx.js'; + +const prepareL2Wsteth = async () => { + const l2 = useL2(); + const account = useAccount(); + const { testClient } = useTestL2RpcProvider(); + const wstethAddress = await l2.wsteth.contractAddress(); + + const wstethImpersonated = getContract({ + abi: bridgedWstethAbi, + address: wstethAddress, + client: testClient, + }); + + const bridge = await wstethImpersonated.read.bridge(); + + await testClient.setBalance({ + address: account.address, + value: 100000000000000n, + }); + + await testClient.setBalance({ + address: bridge, + value: 100000000000000n, + }); + + await testClient.request({ + method: 'evm_addAccount' as any, + params: [bridge, 'pass'], + }); + + await testClient.request({ + method: 'personal_unlockAccount' as any, + params: [bridge, 'pass'], + }); + + await wstethImpersonated.write.bridgeMint([account.address, 2000n], { + account: bridge, + chain: testClient.chain, + }); +}; describe('LidoSDKL2', () => { + const l2 = useL2(); + const account = useAccount(); + beforeAll(async () => { + await prepareL2Wsteth(); + }); + test('is correct module', () => { expectSDKModule(LidoSDKL2); }); + + test('has correct address', async () => { + const address = await l2.contractAddress(); + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + expectAddress(address, stethAddress); + }); + + test('has contract', async () => { + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const contract = await l2.getContract(); + expectContract(contract, stethAddress); + }); + + test('get allowance', async () => { + const allowance = await l2.getWstethForWrapAllowance(account.address); + expectNonNegativeBn(allowance); + + const contractAddress = await l2.contractAddress(); + const altAllowance = await l2.wsteth.allowance({ + account: account.address, + to: contractAddress, + }); + expect(allowance).toEqual(altAllowance); + }); }); -describe('LidoSDKL2Wsteth & LidoSDKL2Steth', () => { +describe('LidoSDKL2 wrap', () => { const l2 = useL2(); - const l2Rpc = useL2Rpc(); const account = useAccount(); - const { testClient } = useTestL2RpcProvider(); - beforeAll(async () => { - const wstethAddress = await l2.wsteth.contractAddress(); + const value = 100n; - const wstethImpersonated = getContract({ - abi: bridgedWstethAbi, - address: wstethAddress, - client: testClient, - }); + beforeAll(prepareL2Wsteth); + + testSpending('set allowance', async () => { + const mock = jest.fn(); + const tx = await l2.approveWstethForWrap({ value, callback: mock }); + expectTxCallback(mock, tx); + await expect(l2.getWstethForWrapAllowance(account)).resolves.toEqual(value); + }); - const bridge = await wstethImpersonated.read.bridge(); + testSpending('wrap populate', async () => { + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const tx = await l2.wrapWstethToStethPopulateTx({ value }); + expectAddress(tx.to, stethAddress); + expectAddress(tx.from, account.address); + expectPopulatedTx(tx, undefined); + await expectPopulatedTxToRun(tx, l2.core.rpcProvider); + }); - await testClient.setBalance({ - address: account.address, - value: 100000000000000n, - }); + testSpending('wrap simulate', async () => { + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const tx = await l2.wrapWstethToStethSimulateTx({ value }); + expectAddress(tx.address, stethAddress); + }); - await testClient.setBalance({ - address: bridge, - value: 100000000000000n, - }); + testSpending('wrap wsteth to steth', async () => { + const stethValue = await l2.steth.convertToSteth(value); + const stethBalanceBefore = await l2.steth.balance(account.address); + const wstethBalanceBefore = await l2.wsteth.balance(account.address); + const mock = jest.fn(); + const tx = await l2.wrapWstethToSteth({ value, callback: mock }); + expectTxCallback(mock, tx); + const stethBalanceAfter = await l2.steth.balance(account.address); + const wstethBalanceAfter = await l2.wsteth.balance(account.address); - await testClient.request({ - method: 'evm_addAccount' as any, - params: [bridge, 'pass'], - }); + const stethDiff = stethBalanceAfter - stethBalanceBefore; + const wstethDiff = wstethBalanceAfter - wstethBalanceBefore; - await testClient.request({ - method: 'personal_unlockAccount' as any, - params: [bridge, 'pass'], - }); + expectAlmostEqualBn(stethDiff, stethValue); + expectAlmostEqualBn(wstethDiff, -value); - await wstethImpersonated.write.bridgeMint([account.address, 2000n], { - account: bridge, - chain: testClient.chain, - }); + expect(tx.result).toBeDefined(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const result = tx.result!; + expectAlmostEqualBn(result.stethReceived, stethDiff); + expect(result.wstethWrapped).toEqual(-wstethDiff); - await l2.approveWstethForWrap({ value: 1000n, account }); - await l2.wrapWstethToSteth({ value: 1000n, account }); + await expect( + l2.getWstethForWrapAllowance(account.address), + ).resolves.toEqual(0n); + }); + + testSpending('unwrap steth populate', async () => { + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const tx = await l2.unwrapPopulateTx({ value }); + expectAddress(tx.to, stethAddress); + expectAddress(tx.from, account.address); + expectPopulatedTx(tx, undefined); + await expectPopulatedTxToRun(tx, l2.core.rpcProvider); + }); + + testSpending('unwrap steth simulate', async () => { + const stethAddress = await l2.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const tx = await l2.unwrapSimulateTx({ value }); + expectAddress(tx.address, stethAddress); + }); + + testSpending('unwrap', async () => { + const stethValue = await l2.steth.convertToSteth(value); + const stethBalanceBefore = await l2.steth.balance(account.address); + const wstethBalanceBefore = await l2.wsteth.balance(account.address); + const mock = jest.fn(); + const tx = await l2.unwrap({ value: stethValue, callback: mock }); + expectTxCallback(mock, tx); + const stethBalanceAfter = await l2.steth.balance(account.address); + const wstethBalanceAfter = await l2.wsteth.balance(account.address); + + const stethDiff = stethBalanceAfter - stethBalanceBefore; + const wstethDiff = wstethBalanceAfter - wstethBalanceBefore; + + expectAlmostEqualBn(stethDiff, -stethValue); + expectAlmostEqualBn(wstethDiff, value); + + expect(tx.result).toBeDefined(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { stethUnwrapped, wstethReceived } = tx.result!; + expectAlmostEqualBn(stethUnwrapped, -stethDiff); + expect(wstethReceived).toEqual(wstethDiff); + }); +}); + +describe('LidoSDKL2Wsteth', () => { + const l2 = useL2(); + const l2Rpc = useL2Rpc(); + + beforeAll(async () => { + await prepareL2Wsteth(); }); // wstETH erc20 tests @@ -81,6 +233,19 @@ describe('LidoSDKL2Wsteth & LidoSDKL2Steth', () => { isL2: true, constructedWithRpcCore: l2Rpc.wsteth, }); +}); + +describe('LidoSDKL2Steth', () => { + const l2 = useL2(); + const l2Rpc = useL2Rpc(); + const account = useAccount(); + + beforeAll(async () => { + await prepareL2Wsteth(); + + await l2.approveWstethForWrap({ value: 1000n, account }); + await l2.wrapWstethToSteth({ value: 1000n, account }); + }); // stETH erc20 tests expectERC20({ From ba639151e74ea8e87cc5f9418b460b13b91a2ec8 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 30 Sep 2024 19:56:30 +0700 Subject: [PATCH 10/28] test: permit tests --- packages/sdk/src/l2/tokens.ts | 39 +++++++++-------- .../tests/utils/expect/expect-erc20-wallet.ts | 43 +++++++++++++------ 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/packages/sdk/src/l2/tokens.ts b/packages/sdk/src/l2/tokens.ts index 57e7039d..4a9a3a26 100644 --- a/packages/sdk/src/l2/tokens.ts +++ b/packages/sdk/src/l2/tokens.ts @@ -48,25 +48,34 @@ export class LidoSDKL2Wsteth extends AbstractLidoSDKErc20 { }); } - @Cache(30 * 60 * 1000, ['core.chain.id']) - public async contractVersion(): Promise { - const contract = await this.getL2Contract(); - return contract.read.getContractVersion(); - } - @Cache(30 * 60 * 1000, ['core.chain.id']) public override async erc721Domain(): Promise<{ name: string; version: string; chainId: bigint; - verifyingContract: `0x${string}`; + verifyingContract: Address; + fields: Hash; + salt: Hash; + extensions: readonly bigint[]; }> { - const { name } = await this.erc20Metadata(); + const contract = await this.getL2Contract(); + const [ + fields, + name, + version, + chainId, + verifyingContract, + salt, + extensions, + ] = await contract.read.eip712Domain(); return { - name: name, - version: (await this.contractVersion()).toString(), - chainId: BigInt(this.core.chain.id), - verifyingContract: await this.contractAddress(), + fields, + name, + version, + chainId, + verifyingContract, + salt, + extensions, }; } } @@ -92,12 +101,6 @@ export class LidoSDKL2Steth extends AbstractLidoSDKErc20 { }); } - @Cache(30 * 60 * 1000, ['core.chain.id']) - public async contractVersion(): Promise { - const contract = await this.getL2Contract(); - return contract.read.getContractVersion(); - } - @Cache(30 * 60 * 1000, ['core.chain.id']) public override async erc721Domain(): Promise<{ name: string; diff --git a/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts b/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts index 330893d0..e4e039e4 100644 --- a/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts +++ b/packages/sdk/tests/utils/expect/expect-erc20-wallet.ts @@ -1,4 +1,4 @@ -import { encodeFunctionData, getContract } from 'viem'; +import { encodeFunctionData, getContract, maxUint256 } from 'viem'; import { expect, describe, test, jest } from '@jest/globals'; import { AbstractLidoSDKErc20 } from '../../../src/erc20/erc20.js'; import { @@ -275,37 +275,51 @@ export const expectERC20Wallet = ({ describe('permit', () => { test('signPermit', async () => { const { address } = useAccount(); + const altAccount = useAltAccount(); const contract = await getTokenContract(); const nonce = await contract.read.nonces([address]); const chainId = token.core.chainId; const params = { amount: 100n, - spender: address, - deadline: 86400n, + spender: altAccount.address, + deadline: maxUint256, }; - const tx = await token.signPermit(params); + const signedPermit = await token.signPermit(params); - expect(tx).toHaveProperty('v'); - expect(tx).toHaveProperty('r'); - expect(tx).toHaveProperty('s'); - expect(tx).toHaveProperty('chainId'); + expect(signedPermit).toHaveProperty('v'); + expect(signedPermit).toHaveProperty('r'); + expect(signedPermit).toHaveProperty('s'); + expect(signedPermit).toHaveProperty('chainId'); - expect(typeof tx.v).toBe('number'); - expect(typeof tx.r).toBe('string'); - expect(typeof tx.s).toBe('string'); - expect(tx.chainId).toBe(BigInt(chainId)); + expect(typeof signedPermit.v).toBe('number'); + expect(typeof signedPermit.r).toBe('string'); + expect(typeof signedPermit.s).toBe('string'); + expect(signedPermit.chainId).toBe(BigInt(chainId)); - const { v, r, s, chainId: _, ...permitMessage } = tx; + const { v, r, s, chainId: _, ...permitMessage } = signedPermit; expectPermitMessage(permitMessage, { address: address, - spender: address, + spender: altAccount.address, amount: params.amount, nonce, deadline: params.deadline, }); + + await contract.simulate.permit( + [ + permitMessage.owner, + permitMessage.spender, + permitMessage.value, + permitMessage.deadline, + v, + r, + s, + ], + { account: altAccount, maxFeePerGas: 0n, maxPriorityFeePerGas: 0n }, + ); }); testSpending('populatePermit', async () => { @@ -334,6 +348,7 @@ export const expectERC20Wallet = ({ expect(tx.domain).toMatchObject(domain); expect(tx.types).toBe(PERMIT_MESSAGE_TYPES); expect(tx.primaryType).toBe('Permit'); + expectPermitMessage(tx.message, { address: address, spender: address, From a134396ed47c74f194f5df8b49e0cc95cc6ac751 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 30 Sep 2024 20:06:24 +0700 Subject: [PATCH 11/28] test: fix permit for v2 --- packages/sdk/src/l2/tokens.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/l2/tokens.ts b/packages/sdk/src/l2/tokens.ts index 4a9a3a26..1882106b 100644 --- a/packages/sdk/src/l2/tokens.ts +++ b/packages/sdk/src/l2/tokens.ts @@ -8,7 +8,7 @@ import { Hash, } from 'viem'; -import { LIDO_L2_CONTRACT_NAMES, NOOP } from '../common/constants.js'; +import { CHAINS, LIDO_L2_CONTRACT_NAMES, NOOP } from '../common/constants.js'; import { parseValue } from '../common/utils/parse-value.js'; import { Cache, ErrorHandler, Logger } from '../common/decorators/index.js'; import { AbstractLidoSDKErc20 } from '../erc20/erc20.js'; @@ -68,10 +68,14 @@ export class LidoSDKL2Wsteth extends AbstractLidoSDKErc20 { salt, extensions, ] = await contract.read.eip712Domain(); + + const fixedVersion = + this.core.chainId === CHAINS.OptimismSepolia ? '1' : version; + return { fields, name, - version, + version: fixedVersion, chainId, verifyingContract, salt, From 09a6b34419f0b2b4f9ace1f4edf7b546e0ae7037 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Tue, 1 Oct 2024 15:15:15 +0700 Subject: [PATCH 12/28] test: l2 shares test --- packages/sdk/src/l2/__test__/l2.test.ts | 80 ++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/l2/__test__/l2.test.ts b/packages/sdk/src/l2/__test__/l2.test.ts index c75d9d3f..6d463c21 100644 --- a/packages/sdk/src/l2/__test__/l2.test.ts +++ b/packages/sdk/src/l2/__test__/l2.test.ts @@ -10,7 +10,10 @@ import { LidoSDKL2Steth, LidoSDKL2Wsteth } from '../tokens.js'; import { expectERC20 } from '../../../tests/utils/expect/expect-erc20.js'; import { LIDO_L2_CONTRACT_NAMES } from '../../common/constants.js'; import { expectERC20Wallet } from '../../../tests/utils/expect/expect-erc20-wallet.js'; -import { useAccount } from '../../../tests/utils/fixtures/use-wallet-client.js'; +import { + useAccount, + useAltAccount, +} from '../../../tests/utils/fixtures/use-wallet-client.js'; import { getContract } from 'viem'; import { bridgedWstethAbi } from '../abi/brigedWsteth.js'; import { expectAddress } from '../../../tests/utils/expect/expect-address.js'; @@ -19,7 +22,10 @@ import { expectAlmostEqualBn, expectNonNegativeBn, } from '../../../tests/utils/expect/expect-bn.js'; -import { testSpending } from '../../../tests/utils/test-spending.js'; +import { + SPENDING_TIMEOUT, + testSpending, +} from '../../../tests/utils/test-spending.js'; import { expectTxCallback } from '../../../tests/utils/expect/expect-tx-callback.js'; import { expectPopulatedTx, @@ -263,3 +269,73 @@ describe('LidoSDKL2Steth', () => { constructedWithRpcCore: l2Rpc.steth, }); }); + +describe('LidoSDKL2Steth shares', () => { + const l2 = useL2(); + const account = useAccount(); + const { address: altAddress } = useAltAccount(); + const value = 1000n; + + beforeAll(async () => { + await prepareL2Wsteth(); + + await l2.approveWstethForWrap({ value, account }); + await l2.wrapWstethToSteth({ value, account }); + }); + + test('shares balance and conversions', async () => { + const balanceSteth = await l2.steth.balance(); + const shares = await l2.steth.balanceShares(account); + + const convertedToShares = await l2.steth.convertToShares(balanceSteth); + expectAlmostEqualBn(shares, convertedToShares); + const convertedToSteth = await l2.steth.convertToSteth(shares); + expectAlmostEqualBn(balanceSteth, convertedToSteth); + }); + + test('populate transfer', async () => { + const tx = await l2.steth.populateTransferShares({ + to: altAddress, + amount: 100n, + }); + expectPopulatedTx(tx, undefined, true); + await expectPopulatedTxToRun(tx, l2.core.rpcProvider); + }); + + test('simulate transfer', async () => { + const contractAddressSteth = await l2.steth.core.getL2ContractAddress( + LIDO_L2_CONTRACT_NAMES.steth, + ); + const tx = await l2.steth.simulateTransferShares({ + to: altAddress, + amount: 100n, + }); + expectAddress(tx.request.address, contractAddressSteth); + expectAddress(tx.request.functionName, 'transferShares'); + }); + + testSpending( + 'can transfer shares', + async () => { + const amount = 100n; + const amountSteth = await l2.steth.convertToSteth(amount); + const balanceStethBefore = await l2.steth.balance(account.address); + const balanceSharesBefore = await l2.steth.balanceShares(account.address); + const mockTxCallback = jest.fn(); + + const tx = await l2.steth.transferShares({ + amount, + to: altAddress, + callback: mockTxCallback, + }); + expectTxCallback(mockTxCallback, tx); + + const balanceStethAfter = await l2.steth.balance(account.address); + const balanceSharesAfter = await l2.steth.balanceShares(account.address); + expect(balanceSharesAfter - balanceSharesBefore).toEqual(-amount); + // due to protocol rounding error this can happen + expectAlmostEqualBn(balanceStethAfter - balanceStethBefore, -amountSteth); + }, + SPENDING_TIMEOUT, + ); +}); From cd2b597a147341873c5a97158e67e832c3b18368 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Tue, 1 Oct 2024 15:17:35 +0700 Subject: [PATCH 13/28] chore: misconfiguration comment --- packages/sdk/src/l2/tokens.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk/src/l2/tokens.ts b/packages/sdk/src/l2/tokens.ts index 1882106b..d2ff086b 100644 --- a/packages/sdk/src/l2/tokens.ts +++ b/packages/sdk/src/l2/tokens.ts @@ -69,6 +69,7 @@ export class LidoSDKL2Wsteth extends AbstractLidoSDKErc20 { extensions, ] = await contract.read.eip712Domain(); + // Testnet misconfiguration const fixedVersion = this.core.chainId === CHAINS.OptimismSepolia ? '1' : version; From a8e5f292a2b0c073b08bec5cdff40e1de507bb97 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Tue, 1 Oct 2024 15:31:39 +0700 Subject: [PATCH 14/28] test: lint --- packages/sdk/src/l2/__test__/l2.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/sdk/src/l2/__test__/l2.test.ts b/packages/sdk/src/l2/__test__/l2.test.ts index 6d463c21..1df82a94 100644 --- a/packages/sdk/src/l2/__test__/l2.test.ts +++ b/packages/sdk/src/l2/__test__/l2.test.ts @@ -85,14 +85,14 @@ describe('LidoSDKL2', () => { test('has correct address', async () => { const address = await l2.contractAddress(); - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); expectAddress(address, stethAddress); }); test('has contract', async () => { - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const contract = await l2.getContract(); @@ -128,7 +128,7 @@ describe('LidoSDKL2 wrap', () => { }); testSpending('wrap populate', async () => { - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const tx = await l2.wrapWstethToStethPopulateTx({ value }); @@ -139,7 +139,7 @@ describe('LidoSDKL2 wrap', () => { }); testSpending('wrap simulate', async () => { - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const tx = await l2.wrapWstethToStethSimulateTx({ value }); @@ -174,7 +174,7 @@ describe('LidoSDKL2 wrap', () => { }); testSpending('unwrap steth populate', async () => { - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const tx = await l2.unwrapPopulateTx({ value }); @@ -185,7 +185,7 @@ describe('LidoSDKL2 wrap', () => { }); testSpending('unwrap steth simulate', async () => { - const stethAddress = await l2.core.getL2ContractAddress( + const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const tx = await l2.unwrapSimulateTx({ value }); @@ -303,7 +303,7 @@ describe('LidoSDKL2Steth shares', () => { }); test('simulate transfer', async () => { - const contractAddressSteth = await l2.steth.core.getL2ContractAddress( + const contractAddressSteth = l2.steth.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); const tx = await l2.steth.simulateTransferShares({ From 3086fac18a7f50a070dbfb53eb5afe5422b5b8f7 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Tue, 1 Oct 2024 16:04:36 +0700 Subject: [PATCH 15/28] build(ci): add envs --- .github/workflows/checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8c7397b9..76746033 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -40,4 +40,6 @@ jobs: TEST_PRIVATE_KEY: ${{ secrets.TEST_PRIVATE_KEY }} TEST_CHAIN_ID: ${{ vars.TEST_CHAIN_ID }} TEST_RPC_URL: ${{ secrets.TEST_RPC_URL }} + TEST_L2_CHAIN_ID: ${{ vars.TEST_L2_CHAIN_ID }} + TEST_L2_RPC_URL: ${{ secrets.TEST_L2_RPC_URL }} TEST_SUBGRAPH_URL: ${{ secrets.TEST_SUBGRAPH_URL }} From 10c816f743579bae78f0224cca029ddf45c90f77 Mon Sep 17 00:00:00 2001 From: DiRaiks Date: Wed, 2 Oct 2024 12:14:41 +0300 Subject: [PATCH 16/28] fix: update pre-publish wf --- .github/workflows/publish-dry-run.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-dry-run.yml b/.github/workflows/publish-dry-run.yml index 1d5fe077..5184a811 100644 --- a/.github/workflows/publish-dry-run.yml +++ b/.github/workflows/publish-dry-run.yml @@ -7,10 +7,15 @@ on: description: 'NPM token' required: true +permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + id-token: write # to enable use of OIDC for npm provenance + jobs: publish-dry-run: runs-on: ubuntu-latest - environment: development + environment: pre-publish steps: - name: Checkout repo uses: actions/checkout@v4 From a7d9c00abfd11c63efc5b0121ed5931cf921517f Mon Sep 17 00:00:00 2001 From: DiRaiks Date: Wed, 2 Oct 2024 12:21:40 +0300 Subject: [PATCH 17/28] fix: check wf --- .github/workflows/publish-dry-run.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-dry-run.yml b/.github/workflows/publish-dry-run.yml index 5184a811..c390df37 100644 --- a/.github/workflows/publish-dry-run.yml +++ b/.github/workflows/publish-dry-run.yml @@ -36,7 +36,7 @@ jobs: run: yarn build:packages - name: Dry run Publish - run: yarn multi-semantic-release --dry-run --silent | grep -E '#|###|\*' > dry_run_output.txt + run: yarn multi-semantic-release --dry-run env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 840fe8f420bca585ba12034d5d6c57c14b10fee0 Mon Sep 17 00:00:00 2001 From: DiRaiks Date: Wed, 2 Oct 2024 12:24:44 +0300 Subject: [PATCH 18/28] fix: remove permissions --- .github/workflows/publish-dry-run.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/publish-dry-run.yml b/.github/workflows/publish-dry-run.yml index c390df37..7e79061d 100644 --- a/.github/workflows/publish-dry-run.yml +++ b/.github/workflows/publish-dry-run.yml @@ -7,11 +7,6 @@ on: description: 'NPM token' required: true -permissions: - contents: write # to be able to publish a GitHub release - issues: write # to be able to comment on released issues - id-token: write # to enable use of OIDC for npm provenance - jobs: publish-dry-run: runs-on: ubuntu-latest @@ -36,7 +31,7 @@ jobs: run: yarn build:packages - name: Dry run Publish - run: yarn multi-semantic-release --dry-run + run: yarn multi-semantic-release --dry-run --silent | grep -E '#|###|\*' > dry_run_output.txt env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From fe09ca97d3e16becff5c3dbdbb009402cc117e14 Mon Sep 17 00:00:00 2001 From: DiRaiks Date: Wed, 2 Oct 2024 19:13:31 +0300 Subject: [PATCH 19/28] fix: update pre-publish wf --- .github/workflows/publish-dry-run.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-dry-run.yml b/.github/workflows/publish-dry-run.yml index 7e79061d..2c8996f5 100644 --- a/.github/workflows/publish-dry-run.yml +++ b/.github/workflows/publish-dry-run.yml @@ -31,14 +31,16 @@ jobs: run: yarn build:packages - name: Dry run Publish - run: yarn multi-semantic-release --dry-run --silent | grep -E '#|###|\*' > dry_run_output.txt + run: | + yarn multi-semantic-release --dry-run --silent > /tmp/multi-semantic-release-output + grep -E '#|###|\*' /tmp/multi-semantic-release-output > dry_run_output.txt || [ $? -eq 1 ] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Write results to summary run: | - if [ -s dry_run_output.txt ]; then + if [ -s dry_run_output.txt ]; then echo "# Packages to be published:" >> $GITHUB_STEP_SUMMARY cat dry_run_output.txt >> $GITHUB_STEP_SUMMARY else From d03341069c63c0c00a2312c57e493b84835a14a2 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 2 Oct 2024 23:26:47 +0700 Subject: [PATCH 20/28] fix: add decorator --- packages/sdk/src/l2/l2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sdk/src/l2/l2.ts b/packages/sdk/src/l2/l2.ts index 7d454045..0bb6a141 100644 --- a/packages/sdk/src/l2/l2.ts +++ b/packages/sdk/src/l2/l2.ts @@ -218,6 +218,7 @@ export class LidoSDKL2 extends LidoSDKModule { } @Logger('Utils:') + @ErrorHandler() public async unwrapPopulateTx( props: Omit, ): Promise { From 56ec2cfeb2b1f3948832f84480508a152333ff63 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 3 Oct 2024 16:40:44 +0700 Subject: [PATCH 21/28] docs: add l2 to docs --- docs/sdk/{methods => modules}/_category_.json | 2 +- docs/sdk/modules/l2.md | 109 ++++++++++++++++++ .../lido-contract-addresses.md | 0 docs/sdk/{methods => modules}/lido-events.md | 0 .../{methods => modules}/lido-statistics.md | 0 docs/sdk/{methods => modules}/rewards.md | 0 docs/sdk/{methods => modules}/shares.md | 0 docs/sdk/{methods => modules}/stake.md | 0 docs/sdk/{methods => modules}/unsteth-nft.md | 0 docs/sdk/{methods => modules}/w-steth.md | 0 docs/sdk/{methods => modules}/withdraw.md | 0 docs/sdk/{methods => modules}/wrap.md | 0 12 files changed, 110 insertions(+), 1 deletion(-) rename docs/sdk/{methods => modules}/_category_.json (75%) create mode 100644 docs/sdk/modules/l2.md rename docs/sdk/{methods => modules}/lido-contract-addresses.md (100%) rename docs/sdk/{methods => modules}/lido-events.md (100%) rename docs/sdk/{methods => modules}/lido-statistics.md (100%) rename docs/sdk/{methods => modules}/rewards.md (100%) rename docs/sdk/{methods => modules}/shares.md (100%) rename docs/sdk/{methods => modules}/stake.md (100%) rename docs/sdk/{methods => modules}/unsteth-nft.md (100%) rename docs/sdk/{methods => modules}/w-steth.md (100%) rename docs/sdk/{methods => modules}/withdraw.md (100%) rename docs/sdk/{methods => modules}/wrap.md (100%) diff --git a/docs/sdk/methods/_category_.json b/docs/sdk/modules/_category_.json similarity index 75% rename from docs/sdk/methods/_category_.json rename to docs/sdk/modules/_category_.json index 5c995b5f..2e2e35b8 100644 --- a/docs/sdk/methods/_category_.json +++ b/docs/sdk/modules/_category_.json @@ -1,5 +1,5 @@ { - "label": "Methods", + "label": "Modules", "position": 3, "link": { "type": "generated-index" diff --git a/docs/sdk/modules/l2.md b/docs/sdk/modules/l2.md new file mode 100644 index 00000000..2123a7f7 --- /dev/null +++ b/docs/sdk/modules/l2.md @@ -0,0 +1,109 @@ +--- +sidebar_position: 14 +--- + +# L2 + +Modules exposes Lido MultiChain deployments. [See full info here](https://lido.fi/lido-multichain). + +## LidoSDKL2 + +This is core module for all L2 functionality. It will throw error if used on with chains that are not currently supported. + +### Static methods and fields + +- `LidoSDKL2.isContractAvailableOn(contract,chainId)`: use this helper to understand which contracts are supported on chain +- `wsteth`: see [LidoSDKL2Wsteth](#lidosdkl2wsteth) +- `steth`: see [LidoSDKL2Steth](#lidosdkl2steth) + +### Methods + +On L2 with stETH deployments bridged wstETH is wrapped to stETH. And stETH is unwrapped to wstETH. Those semantics are upkept in SDK with more explicit naming. See [LIP-22](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-22.md#rebasable-token-steth-on-l2) for more details. + +### Wrap bridged wstETH to stETH + +To wrap stETH you first need to approve stETH to wrap contract: + +```ts +import { LidoSDK } from '@lidofinance/lido-ethereum-sdk'; + +const lidoSDK = new LidoSDK({ + rpcUrls: ['https://rpc-url'], + chainId: 11155420, // OP sepolia + web3Provider: LidoSDKCore.createWeb3Provider(11155420, window.ethereum), +}); + +// get existing allowance +const allowance = await lidoSDK.l2.getWstethForWrapAllowance(); + +// if value is more than allowance perform approve +if (allowance < value) { + const approveResult = await lidoSDK.wrap.approveWstethForWrap({ + value, + callback, + }); +} + +// wrap wstETH +const wrapTx = await lidoSDK.wrap.wrapWstethToSteth({ value, callback }); + +const { stethReceived, wstethWrapped } = wrapTx.results; +``` + +### Unwrap stETH to wstETH + +```ts +// unwrap stETH to receive wstETH +const unwrapTx = await lidoSDK.l2.unwrapStethToWsteth({ + value: unwrapAmount, + callback, +}); + +console.log(unwrapTx.result.stethUnwrapped, unwrapTx.result.wstethReceived); +``` + +## Wrap utilities + +For all transaction methods helper methods are available similar to `stake` module: + +- `...populateTX`: returns ready to sign transaction object with all data encoded +- `...simulateTX`: performs dry-ran of the transaction to see if it will execute on the network + +## LidoSDKL2Wsteth + +This submodule is built on top of existing ERC20 modules and has extra functionality. See docs for all [ERC20 related methods](./w-steth.md). +For original L2 ABI functionality use `.getL2Contract()` and get raw Viem contract instance. + +## LidoSDKL2Steth + +This submodule is built on top of existing ERC20 modules but has extra L2 stETH related features. See docs for all [ERC20 related methods](./w-steth.md). +For original L2 ABI functionality use `.getL2Contract()` and get raw Viem contract instance. + +```ts +import { LidoSDK } from '@lidofinance/lido-ethereum-sdk'; + +const lidoSDK = new LidoSDK({ + rpcUrls: ['https://rpc-url'], + chainId: 11155420, // OP sepolia + web3Provider: LidoSDKCore.createWeb3Provider(11155420, window.ethereum), +}); + +// balance of stETH for account in shares +const balanceShares = await lidoSDK.l2.steth.balanceShares(address); + +// transferring shares is equivalent to transferring corresponding amount of stETH +const transferTx = await lidoSDK.l2.steth.transferShares({ + account, + amount, + to, +}); + +// converting stETH amount to shares trough on-chain call based on actual share rate +// This also can be used to convert stETH to wstETH as 1 wstETH = 1 share +const shares = await lidoSDK.l2.steth.convertToShares(1000n); +// reverse +const steth = await lidoSDK.l2.steth.convertToSteth(1000n); + +// total supply of shares and ether in protocol +const totalShares = await lidoSDK.totalShares(); +``` diff --git a/docs/sdk/methods/lido-contract-addresses.md b/docs/sdk/modules/lido-contract-addresses.md similarity index 100% rename from docs/sdk/methods/lido-contract-addresses.md rename to docs/sdk/modules/lido-contract-addresses.md diff --git a/docs/sdk/methods/lido-events.md b/docs/sdk/modules/lido-events.md similarity index 100% rename from docs/sdk/methods/lido-events.md rename to docs/sdk/modules/lido-events.md diff --git a/docs/sdk/methods/lido-statistics.md b/docs/sdk/modules/lido-statistics.md similarity index 100% rename from docs/sdk/methods/lido-statistics.md rename to docs/sdk/modules/lido-statistics.md diff --git a/docs/sdk/methods/rewards.md b/docs/sdk/modules/rewards.md similarity index 100% rename from docs/sdk/methods/rewards.md rename to docs/sdk/modules/rewards.md diff --git a/docs/sdk/methods/shares.md b/docs/sdk/modules/shares.md similarity index 100% rename from docs/sdk/methods/shares.md rename to docs/sdk/modules/shares.md diff --git a/docs/sdk/methods/stake.md b/docs/sdk/modules/stake.md similarity index 100% rename from docs/sdk/methods/stake.md rename to docs/sdk/modules/stake.md diff --git a/docs/sdk/methods/unsteth-nft.md b/docs/sdk/modules/unsteth-nft.md similarity index 100% rename from docs/sdk/methods/unsteth-nft.md rename to docs/sdk/modules/unsteth-nft.md diff --git a/docs/sdk/methods/w-steth.md b/docs/sdk/modules/w-steth.md similarity index 100% rename from docs/sdk/methods/w-steth.md rename to docs/sdk/modules/w-steth.md diff --git a/docs/sdk/methods/withdraw.md b/docs/sdk/modules/withdraw.md similarity index 100% rename from docs/sdk/methods/withdraw.md rename to docs/sdk/modules/withdraw.md diff --git a/docs/sdk/methods/wrap.md b/docs/sdk/modules/wrap.md similarity index 100% rename from docs/sdk/methods/wrap.md rename to docs/sdk/modules/wrap.md From ce614940cfea3777c6c2c3925ef53bff997c422a Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 3 Oct 2024 16:40:59 +0700 Subject: [PATCH 22/28] fix: naming for l2 unwrap --- packages/sdk/src/l2/__test__/l2.test.ts | 9 ++++++--- packages/sdk/src/l2/l2.ts | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/sdk/src/l2/__test__/l2.test.ts b/packages/sdk/src/l2/__test__/l2.test.ts index 1df82a94..1cf4c768 100644 --- a/packages/sdk/src/l2/__test__/l2.test.ts +++ b/packages/sdk/src/l2/__test__/l2.test.ts @@ -177,7 +177,7 @@ describe('LidoSDKL2 wrap', () => { const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); - const tx = await l2.unwrapPopulateTx({ value }); + const tx = await l2.unwrapStethPopulateTx({ value }); expectAddress(tx.to, stethAddress); expectAddress(tx.from, account.address); expectPopulatedTx(tx, undefined); @@ -188,7 +188,7 @@ describe('LidoSDKL2 wrap', () => { const stethAddress = l2.core.getL2ContractAddress( LIDO_L2_CONTRACT_NAMES.steth, ); - const tx = await l2.unwrapSimulateTx({ value }); + const tx = await l2.unwrapStethSimulateTx({ value }); expectAddress(tx.address, stethAddress); }); @@ -197,7 +197,10 @@ describe('LidoSDKL2 wrap', () => { const stethBalanceBefore = await l2.steth.balance(account.address); const wstethBalanceBefore = await l2.wsteth.balance(account.address); const mock = jest.fn(); - const tx = await l2.unwrap({ value: stethValue, callback: mock }); + const tx = await l2.unwrapStethToWsteth({ + value: stethValue, + callback: mock, + }); expectTxCallback(mock, tx); const stethBalanceAfter = await l2.steth.balance(account.address); const wstethBalanceAfter = await l2.wsteth.balance(account.address); diff --git a/packages/sdk/src/l2/l2.ts b/packages/sdk/src/l2/l2.ts index 0bb6a141..28667ad4 100644 --- a/packages/sdk/src/l2/l2.ts +++ b/packages/sdk/src/l2/l2.ts @@ -199,7 +199,7 @@ export class LidoSDKL2 extends LidoSDKModule { @Logger('Call:') @ErrorHandler() - public async unwrap( + public async unwrapStethToWsteth( props: WrapProps, ): Promise> { this.core.useWeb3Provider(); @@ -219,7 +219,7 @@ export class LidoSDKL2 extends LidoSDKModule { @Logger('Utils:') @ErrorHandler() - public async unwrapPopulateTx( + public async unwrapStethPopulateTx( props: Omit, ): Promise { const { value, account } = await this.parseProps(props); @@ -238,7 +238,7 @@ export class LidoSDKL2 extends LidoSDKModule { @Logger('Call:') @ErrorHandler() - public async unwrapSimulateTx( + public async unwrapStethSimulateTx( props: Omit, ): Promise { const { value, account } = await this.parseProps(props); From 14b31dc9edd34bc16f447646ee056ca101da8133 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 3 Oct 2024 16:42:21 +0700 Subject: [PATCH 23/28] fix: demo --- playground/demo/l2/wrap-l2.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playground/demo/l2/wrap-l2.tsx b/playground/demo/l2/wrap-l2.tsx index cae3862a..54b4e3d8 100644 --- a/playground/demo/l2/wrap-l2.tsx +++ b/playground/demo/l2/wrap-l2.tsx @@ -87,7 +87,7 @@ export const WrapL2Demo = () => { walletAction title="Unwrap stETH to wstETH" action={() => - l2.unwrap({ + l2.unwrapStethToWsteth({ value: stethValue ?? ZERO, callback: transactionToast, @@ -105,7 +105,7 @@ export const WrapL2Demo = () => { title="Populate unwrap" walletAction action={() => - l2.unwrapPopulateTx({ + l2.unwrapStethPopulateTx({ value: stethValue ?? ZERO, }) } @@ -114,7 +114,7 @@ export const WrapL2Demo = () => { title="Simulate unwrap" walletAction action={() => - l2.unwrapSimulateTx({ + l2.unwrapStethSimulateTx({ value: stethValue ?? ZERO, }) } From da66ff4c5651ea23380eedea635bae6e165e0bc8 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 4 Oct 2024 13:50:47 +0700 Subject: [PATCH 24/28] feat: op mainnet --- docs/sdk/modules/l2.md | 32 +++++++++++++++++++++++----- packages/sdk/MIGRATION.md | 4 ++++ packages/sdk/src/common/constants.ts | 3 ++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/sdk/modules/l2.md b/docs/sdk/modules/l2.md index 2123a7f7..714e78b5 100644 --- a/docs/sdk/modules/l2.md +++ b/docs/sdk/modules/l2.md @@ -10,9 +10,31 @@ Modules exposes Lido MultiChain deployments. [See full info here](https://lido.f This is core module for all L2 functionality. It will throw error if used on with chains that are not currently supported. -### Static methods and fields +| **Chain** | **wsETH** | **stETH+(un)Wrap** | +| ---------------- | --------- | ------------------ | +| Optimism Sepolia | ✅ | ✅ | +| Optmism | ✅ | ✅ | +| 🔜 | | | + +Use this helper to understand which contracts are supported on chain: + +```ts +import { + LidoSDKL2, + LIDO_L2_CONTRACT_NAMES, + CHAINS, +} from '@lidofinance/lido-ethereum-sdk'; + +LidoSDKL2.isContractAvailableOn( + LIDO_L2_CONTRACT_NAMES.wsteth, + CHAINS.OptimismSepolia, +); // true +// Example +LidoSDKL2.isContractAvailableOn(LIDO_L2_CONTRACT_NAMES.steth, CHAINS.Arbitrum); // false +``` + +### Fields -- `LidoSDKL2.isContractAvailableOn(contract,chainId)`: use this helper to understand which contracts are supported on chain - `wsteth`: see [LidoSDKL2Wsteth](#lidosdkl2wsteth) - `steth`: see [LidoSDKL2Steth](#lidosdkl2steth) @@ -20,7 +42,7 @@ This is core module for all L2 functionality. It will throw error if used on wit On L2 with stETH deployments bridged wstETH is wrapped to stETH. And stETH is unwrapped to wstETH. Those semantics are upkept in SDK with more explicit naming. See [LIP-22](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-22.md#rebasable-token-steth-on-l2) for more details. -### Wrap bridged wstETH to stETH +#### Wrap bridged wstETH to stETH To wrap stETH you first need to approve stETH to wrap contract: @@ -50,7 +72,7 @@ const wrapTx = await lidoSDK.wrap.wrapWstethToSteth({ value, callback }); const { stethReceived, wstethWrapped } = wrapTx.results; ``` -### Unwrap stETH to wstETH +#### Unwrap stETH to wstETH ```ts // unwrap stETH to receive wstETH @@ -62,7 +84,7 @@ const unwrapTx = await lidoSDK.l2.unwrapStethToWsteth({ console.log(unwrapTx.result.stethUnwrapped, unwrapTx.result.wstethReceived); ``` -## Wrap utilities +### Wrap utilities For all transaction methods helper methods are available similar to `stake` module: diff --git a/packages/sdk/MIGRATION.md b/packages/sdk/MIGRATION.md index ed23239b..206b19a9 100644 --- a/packages/sdk/MIGRATION.md +++ b/packages/sdk/MIGRATION.md @@ -1,3 +1,7 @@ +# Migrating from V3 -> V4 + +- `viem` is now a peer dependency and you will need to install it separately. + # Migrating from V2 -> V3 ## Common diff --git a/packages/sdk/src/common/constants.ts b/packages/sdk/src/common/constants.ts index c5dac6c2..e1cfe09c 100644 --- a/packages/sdk/src/common/constants.ts +++ b/packages/sdk/src/common/constants.ts @@ -24,12 +24,12 @@ export const SUPPORTED_CHAINS: CHAINS[] = [ CHAINS.Mainnet, CHAINS.Holesky, CHAINS.Sepolia, + CHAINS.Optimism, CHAINS.OptimismSepolia, ]; export const SUBMIT_EXTRA_GAS_TRANSACTION_RATIO = 1.05; export const GAS_TRANSACTION_RATIO_PRECISION = 10 ** 7; -export const ESTIMATE_ACCOUNT = '0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1'; export const LIDO_LOCATOR_BY_CHAIN: { [key in CHAINS]?: Address; @@ -95,6 +95,7 @@ export const LIDO_L2_CONTRACT_ADDRESSES: { }, [CHAINS.Optimism]: { wsteth: '0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb', + steth: '0x76A50b8c7349cCDDb7578c6627e79b5d99D24138', }, }; From 6861aa4322c11b289520279161bc490812af8006 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 4 Oct 2024 14:05:15 +0700 Subject: [PATCH 25/28] docs: fix build --- docs/examples/rewards/intro.md | 20 +++++++++---------- .../rewards/retrieve-rewards-onchain.md | 2 +- .../rewards/retrieve-rewards-subgraph.md | 2 +- docs/examples/rewards/subscribe-on-events.md | 2 +- docs/lidoPulse/get-started/usage.md | 2 +- docs/sdk/intro.md | 2 +- docs/sdk/modules/rewards.md | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/examples/rewards/intro.md b/docs/examples/rewards/intro.md index b651a133..519d8fc8 100644 --- a/docs/examples/rewards/intro.md +++ b/docs/examples/rewards/intro.md @@ -26,7 +26,7 @@ The proposed approach involves maintaining an accounting model based on stETH [s ## Implementation notes Token shares are managed at the contract level, with dedicated methods for handling share-related operations. Detailed documentation on these methods can be found in the [shares-related methods](https://docs.lido.fi/contracts/lido/#shares-related-methods) section. -You can also use [SDK methods](/methods/shares) to work with users’ shares directly. +You can also use [SDK methods](/modules/shares) to work with users’ shares directly. ## Usage @@ -40,13 +40,13 @@ By adopting this approach and leveraging the capabilities of the SDK, developers The [Lido Ethereum SDK](/) has the full set of features in this regard: -- [estimating APR](/methods/lido-statistics#getlastapr) for the latest token rebase. -- [calculating average APR](/methods/lido-statistics#getsmaapr) over a selected period. -- [last rebase event](/methods/lido-events#getlastrebaseevent) (contains share rate) -- [first rebase event](/methods/lido-events#getfirstrebaseevent) starting from the reference point in the past -- [get last N](/methods/lido-events#getlastrebaseevents) rebase events -- [get all rebase events](/methods/lido-events#getrebaseevents) for the last N days +- [estimating APR](/modules/lido-statistics#getlastapr) for the latest token rebase. +- [calculating average APR](/modules/lido-statistics#getsmaapr) over a selected period. +- [last rebase event](/modules/lido-events#getlastrebaseevent) (contains share rate) +- [first rebase event](/modules/lido-events#getfirstrebaseevent) starting from the reference point in the past +- [get last N](/modules/lido-events#getlastrebaseevents) rebase events +- [get all rebase events](/modules/lido-events#getrebaseevents) for the last N days - assessing specific rewards accrued over a chosen period by an address - - [On-chain](/methods/rewards#get-rewards-from-chain) - - [Subgraph](/methods/rewards#get-rewards-from-subgraph) -- work with [user balances](/methods/shares) based on stETH shares + - [On-chain](/modules/rewards#get-rewards-from-chain) + - [Subgraph](/modules/rewards#get-rewards-from-subgraph) +- work with [user balances](/modules/shares) based on stETH shares diff --git a/docs/examples/rewards/retrieve-rewards-onchain.md b/docs/examples/rewards/retrieve-rewards-onchain.md index 19814ee7..fcb90b10 100644 --- a/docs/examples/rewards/retrieve-rewards-onchain.md +++ b/docs/examples/rewards/retrieve-rewards-onchain.md @@ -11,7 +11,7 @@ sidebar_position: 4 [Implementation example](https://github.com/lidofinance/lido-ethereum-sdk/blob/main/examples/rewards/src/rewardsOnChain.ts) Information about the user’s rewards can be calculating from on-chain using SDK without the need to calculate using a formula. -To do this, you need to use the `getRewardsFromChain` method ([Docs](/methods/rewards)) +To do this, you need to use the `getRewardsFromChain` method ([Docs](/modules/rewards)) The method allows you to request rewards for a certain period of time (days, seconds, blocks) Simplified code example: diff --git a/docs/examples/rewards/retrieve-rewards-subgraph.md b/docs/examples/rewards/retrieve-rewards-subgraph.md index ab98ba41..ad0e9947 100644 --- a/docs/examples/rewards/retrieve-rewards-subgraph.md +++ b/docs/examples/rewards/retrieve-rewards-subgraph.md @@ -14,7 +14,7 @@ sidebar_position: 5 [Implementation example](https://github.com/lidofinance/lido-ethereum-sdk/blob/main/examples/rewards/src/rewardsSubgraph.ts) Information about the user’s rewards can be obtained from off-chain using SDK without the need for calculation using a formula. -To do this, you need to use the `getRewardsFromSubgraph` method [[Docs](/methods/rewards)]. You will also need a key to access `The Graph`. ([Docs](https://docs.lido.fi/integrations/subgraph/)) +To do this, you need to use the `getRewardsFromSubgraph` method [[Docs](/modules/rewards)]. You will also need a key to access `The Graph`. ([Docs](https://docs.lido.fi/integrations/subgraph/)) Simplified code example: diff --git a/docs/examples/rewards/subscribe-on-events.md b/docs/examples/rewards/subscribe-on-events.md index 02186bc1..82c15192 100644 --- a/docs/examples/rewards/subscribe-on-events.md +++ b/docs/examples/rewards/subscribe-on-events.md @@ -20,7 +20,7 @@ The first thing you need to do is subscribe to the `TokenRebased` event to recei Next, you need to calculate the user’s balance in stETH before the event (if unknown) and calculate the user’s balance in stETH after the event. The difference between these values will be the user’s rewards for the rebase. -Docs: [Shares](/methods/shares) +Docs: [Shares](/modules/shares) Simplified code example: ```ts diff --git a/docs/lidoPulse/get-started/usage.md b/docs/lidoPulse/get-started/usage.md index e99ff220..69aacd56 100644 --- a/docs/lidoPulse/get-started/usage.md +++ b/docs/lidoPulse/get-started/usage.md @@ -15,7 +15,7 @@ To form a JSON-RPC request, you need to specify: - `params`: The parameters required by the method. - `id`: A unique identifier for the request. -The method names and their required parameters can be found in the [Lido SDK documentation](/category/methods). +The method names and their required parameters can be found in the [Lido SDK documentation](/category/modules). ## Example JSON-RPC Request diff --git a/docs/sdk/intro.md b/docs/sdk/intro.md index 749aef4d..d329a372 100644 --- a/docs/sdk/intro.md +++ b/docs/sdk/intro.md @@ -107,7 +107,7 @@ For breaking changes between versions see [MIGRATION.md](https://github.com/lido ## Documentation -For additional information about available methods and functionality, refer to the [the documentation for the Lido Ethereum SDK](/category/methods). +For additional information about available methods and functionality, refer to the [the documentation for the Lido Ethereum SDK](/category/modules). ## Playground diff --git a/docs/sdk/modules/rewards.md b/docs/sdk/modules/rewards.md index 33f89fae..f0830897 100644 --- a/docs/sdk/modules/rewards.md +++ b/docs/sdk/modules/rewards.md @@ -10,11 +10,11 @@ y ## Common Options - **address** - (Type: Address) address of an account you want to query rewards for -- **to** (Type: [`blockType`](/methods/lido-events#getrebaseevents)) defaults to `{block: "latest"}`, upper bound for query +- **to** (Type: [`blockType`](/modules/lido-events#getrebaseevents)) defaults to `{block: "latest"}`, upper bound for query -- **from** (Type: [`blockType`](/methods/lido-events#getrebaseevents)) lower bound for query +- **from** (Type: [`blockType`](/modules/lido-events#getrebaseevents)) lower bound for query or -- **back** (Type: [`backType`](/methods/lido-events#getrebaseevents)) alternative way to define lower bound relative to `to` +- **back** (Type: [`backType`](/modules/lido-events#getrebaseevents)) alternative way to define lower bound relative to `to` - **includeZeroRebases** [default: `false` ] - include rebase events when users had no rewards(because of empty balance) - **includeOnlyRewards** [default: `false` ] - include only rebase events From 2bfb069132062d41d7224b977d815fda113870f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:11:52 +0000 Subject: [PATCH 26/28] build(deps): bump find-my-way from 8.2.0 to 8.2.2 Bumps [find-my-way](https://github.com/delvedor/find-my-way) from 8.2.0 to 8.2.2. - [Release notes](https://github.com/delvedor/find-my-way/releases) - [Commits](https://github.com/delvedor/find-my-way/compare/v8.2.0...v8.2.2) --- updated-dependencies: - dependency-name: find-my-way dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b9d50ced..aebcc18e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14448,13 +14448,13 @@ __metadata: linkType: hard "find-my-way@npm:^8.0.0": - version: 8.2.0 - resolution: "find-my-way@npm:8.2.0" + version: 8.2.2 + resolution: "find-my-way@npm:8.2.2" dependencies: fast-deep-equal: ^3.1.3 fast-querystring: ^1.0.0 safe-regex2: ^3.1.0 - checksum: 4f59fe17a1431511ec172403da0d1ac05bf9efebfdd4c7149b658d748b2570b63d798847e08ceea00f57543611fdb64ba3793dfc67a9ed7b5bfa0d77c8693eb5 + checksum: bba4feafece3ef012180e561d904ead38e1f2e089bd97dbcb2c628d38cb869918626c673b08b698d22ef46265a886aa535a650574801ecab9c8eb8db5437736a languageName: node linkType: hard From c00112090234147bcf3edd4df63d06ee3479eff4 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 14 Oct 2024 13:31:40 +0700 Subject: [PATCH 27/28] feat: 3.5.0 version BREAKING CHANGE: viem is now a peer deps --- packages/sdk/CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 2b91e223..43257697 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,3 +1,26 @@ +# 3.5.0 + +## Breaking change + +- `viem` is no longer an internal dependency and is listed as peer dependency + +## SDK + +### Added + +- `LidoSDKL2` module is added to support Lido on L2 networks functionality +- `Optimism` and `Optimism-sepolia` chains are added as separate L2 chains +- `core.getL2ContractAddress` and `LIDO_L2_CONTRACT_NAMES enum` are added to support l2 contracts +- ABIs are exported from corresponding modules to support custom functionality and direct viem access + +### Fixed + +- `multicall` is used only if supported by client + +## Playground + +- L2 and updated reef-knot support + # 3.4.0 ## SDK From 6f02983ea9d7c1b2af92b8dc62c6837c803cedcc Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 14 Oct 2024 13:33:53 +0700 Subject: [PATCH 28/28] docs: add viem to install --- docs/sdk/intro.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sdk/intro.md b/docs/sdk/intro.md index d329a372..883d105c 100644 --- a/docs/sdk/intro.md +++ b/docs/sdk/intro.md @@ -18,11 +18,11 @@ For changes between versions see [CHANGELOG.MD](https://github.com/lidofinance/l ## Installation -You can install the Lido Ethereum SDK using npm or yarn: +You can install the Lido Ethereum SDK using npm or yarn. `viem` is required as a peer dep: ```bash // SDK (stakes, wrap, withdrawals) -yarn add @lidofinance/lido-ethereum-sdk +yarn add viem @lidofinance/lido-ethereum-sdk ``` ## Usage