From a52374a921181ad35286faa817ae5861f87b6e09 Mon Sep 17 00:00:00 2001 From: Baptiste Marchand <75846779+baptiste-marchand@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:42:58 +0200 Subject: [PATCH 1/4] feat: expand EIP155 RPC types and methods --- src/types/scopes/eip155.types.ts | 308 ++++++++++++++++++++++++++++++- 1 file changed, 304 insertions(+), 4 deletions(-) diff --git a/src/types/scopes/eip155.types.ts b/src/types/scopes/eip155.types.ts index eed685c..659bf22 100644 --- a/src/types/scopes/eip155.types.ts +++ b/src/types/scopes/eip155.types.ts @@ -1,10 +1,310 @@ import type { RpcMethod } from '.'; +// Base types +type HexString = `0x${string}`; +type Address = `0x${string}`; +type Hash32 = `0x${string}`; +type BlockTag = 'earliest' | 'finalized' | 'safe' | 'latest' | 'pending'; +type BlockNumberOrTag = HexString | BlockTag; +type BlockNumberOrTagOrHash = HexString | BlockTag | Hash32; + +// Complex types for method parameters and responses +interface AddEthereumChainParameter { + chainId: HexString; + chainName: string; + nativeCurrency: { + name?: string; + symbol: string; + decimals: number; + }; + rpcUrls: string[]; + blockExplorerUrls?: string[]; + iconUrls?: string[]; +} + +interface TypedData { + types: { + EIP712Domain: Array<{ + name: string; + type: string; + }>; + [key: string]: Array<{ + name: string; + type: string; + }>; + }; + primaryType: string; + domain: Record; + message: Record; +} + +interface WatchAssetOptions { + address: string; + symbol?: string; + decimals?: number; + image?: string; + tokenId?: string; +} + +interface Call { + to?: Address; + data?: HexString; + value?: HexString; + capabilities?: Record; +} + +interface SendCallsParameter { + version: string; + id?: string; + from: Address; + chainId: HexString; + atomicRequired: boolean; + calls: Call[]; + capabilities?: Record; +} + +interface BatchResult { + id: string; + capabilities?: Record; +} + +interface BatchStatus { + version: string; + id: string; + chainId: HexString; + status: number; + atomic: boolean; + receipts?: Array<{ + logs: Array<{ + address: Address; + data: HexString; + topics: HexString[]; + }>; + status: HexString; + blockHash: Hash32; + blockNumber: HexString; + gasUsed: HexString; + transactionHash: Hash32; + [key: string]: any; + }>; + capabilities?: Record; +} + +interface Transaction { + from: Address; + to?: Address; + gas?: HexString; + gasPrice?: HexString; + maxFeePerGas?: HexString; + maxPriorityFeePerGas?: HexString; + value?: HexString; + data?: HexString; + nonce?: HexString; + accessList?: Array<{ + address: Address; + storageKeys: Hash32[]; + }>; + type?: HexString; + chainId?: HexString; +} + +interface Filter { + fromBlock?: HexString; + toBlock?: HexString; + address?: Address | Address[]; + topics?: Array; +} + +interface Log { + removed?: boolean; + logIndex?: HexString; + transactionIndex?: HexString; + transactionHash: Hash32; + blockHash?: Hash32; + blockNumber?: HexString; + address: Address; + data: HexString; + topics: HexString[]; +} + +interface Block { + number: HexString; + hash: Hash32; + parentHash: Hash32; + nonce: HexString; + sha3Uncles: Hash32; + logsBloom: HexString; + transactionsRoot: Hash32; + stateRoot: Hash32; + receiptsRoot: Hash32; + miner: Address; + difficulty?: HexString; + totalDifficulty?: HexString; + extraData: HexString; + size: HexString; + gasLimit: HexString; + gasUsed: HexString; + timestamp: HexString; + transactions: Hash32[] | TransactionInfo[]; + uncles: Hash32[]; + baseFeePerGas?: HexString; + withdrawalsRoot?: Hash32; + withdrawals?: Array<{ + index: HexString; + validatorIndex: HexString; + address: Address; + amount: HexString; + }>; + blobGasUsed?: HexString; + excessBlobGas?: HexString; + parentBeaconBlockRoot?: Hash32; + mixHash?: Hash32; +} + +interface TransactionInfo { + blockHash: Hash32; + blockNumber: HexString; + from: Address; + gas: HexString; + gasPrice?: HexString; + maxFeePerGas?: HexString; + maxPriorityFeePerGas?: HexString; + hash: Hash32; + input: HexString; + nonce: HexString; + to?: Address; + transactionIndex: HexString; + value: HexString; + type?: HexString; + accessList?: Array<{ + address: Address; + storageKeys: Hash32[]; + }>; + chainId?: number; + v?: HexString; + r?: HexString; + s?: HexString; + yParity?: HexString; +} + +interface TransactionReceipt { + transactionHash: Hash32; + transactionIndex: HexString; + blockHash: Hash32; + blockNumber: HexString; + from: Address; + to?: Address; + cumulativeGasUsed: HexString; + gasUsed: HexString; + contractAddress?: Address; + logs: Log[]; + logsBloom: HexString; + status?: HexString; + effectiveGasPrice: HexString; + type?: HexString; + blobGasUsed?: HexString; + blobGasPrice?: HexString; +} + +interface FeeHistory { + oldestBlock: HexString; + baseFeePerGas: HexString[]; + baseFeePerBlobGas?: HexString[]; + gasUsedRatio: number[]; + blobGasUsedRatio?: number[]; + reward?: HexString[][]; +} + +interface AccountProof { + address: Address; + accountProof: HexString[]; + balance: HexString; + codeHash: Hash32; + nonce: HexString; + storageHash: Hash32; + storageProof: Array<{ + key: HexString; + value: HexString; + proof: HexString[]; + }>; +} + +type SyncingStatus = + | boolean + | { + startingBlock: HexString; + currentBlock: HexString; + highestBlock: HexString; + }; + export type Eip155Rpc = { methods: { - eth_sendTransaction: RpcMethod<{ to: string; value?: string; data?: string }, string>; - eth_call: RpcMethod<{ to: string; data?: string }, string>; - eth_getBalance: RpcMethod<{ address: string; blockNumber: string }, string>; + // Wallet methods + wallet_addEthereumChain: RpcMethod<[AddEthereumChainParameter], null>; + wallet_watchAsset: RpcMethod<{ type: string; options: WatchAssetOptions }, boolean>; + wallet_scanQRCode: RpcMethod<[string?], string>; + wallet_sendCalls: RpcMethod<[SendCallsParameter], BatchResult>; + wallet_getCallsStatus: RpcMethod<[string], BatchStatus>; + wallet_getCapabilities: RpcMethod<[Address, HexString[]?], Record>; + + // Signing methods + personal_sign: RpcMethod<[HexString, Address], HexString>; + eth_signTypedData_v4: RpcMethod<[Address, TypedData], HexString>; + eth_decrypt: RpcMethod<[string, Address], string>; + eth_getEncryptionPublicKey: RpcMethod<[Address], string>; + + // Account methods + eth_accounts: RpcMethod<[], Address[]>; + + // Transaction methods + eth_sendTransaction: RpcMethod<[Transaction], Hash32>; + eth_sendRawTransaction: RpcMethod<[HexString], Hash32>; + eth_estimateGas: RpcMethod<[Transaction, BlockNumberOrTagOrHash?], HexString>; + + // Block methods + eth_blockNumber: RpcMethod<[], HexString>; + eth_getBlockByHash: RpcMethod<[Hash32, boolean], Block | null>; + eth_getBlockByNumber: RpcMethod<[BlockNumberOrTag, boolean], Block | null>; + eth_getBlockTransactionCountByHash: RpcMethod<[Hash32], HexString | null>; + eth_getBlockTransactionCountByNumber: RpcMethod<[BlockNumberOrTag], HexString | null>; + eth_getUncleCountByBlockHash: RpcMethod<[Hash32], HexString | null>; + eth_getUncleCountByBlockNumber: RpcMethod<[BlockNumberOrTag], HexString | null>; + + // Transaction info methods + eth_getTransactionByHash: RpcMethod<[Hash32], TransactionInfo | null>; + eth_getTransactionByBlockHashAndIndex: RpcMethod<[Hash32, HexString], TransactionInfo | null>; + eth_getTransactionByBlockNumberAndIndex: RpcMethod<[BlockNumberOrTag, HexString], TransactionInfo | null>; + eth_getTransactionCount: RpcMethod<[Address, BlockNumberOrTagOrHash], HexString>; + eth_getTransactionReceipt: RpcMethod<[Hash32], TransactionReceipt | null>; + + // State methods + eth_getBalance: RpcMethod<[Address, BlockNumberOrTagOrHash], HexString>; + eth_getCode: RpcMethod<[Address, BlockNumberOrTagOrHash], HexString>; + eth_getStorageAt: RpcMethod<[Address, HexString, BlockNumberOrTagOrHash], HexString>; + eth_call: RpcMethod<[Transaction, BlockNumberOrTagOrHash?], HexString>; + eth_getProof: RpcMethod<[Address, HexString[], BlockNumberOrTagOrHash], AccountProof>; + + // Gas and fee methods + eth_gasPrice: RpcMethod<[], HexString>; + eth_feeHistory: RpcMethod<[HexString, BlockNumberOrTag, number[]], FeeHistory>; + + // Filter and log methods + eth_newFilter: RpcMethod<[Filter], HexString>; + eth_newBlockFilter: RpcMethod<[], HexString>; + eth_newPendingTransactionFilter: RpcMethod<[], HexString>; + eth_uninstallFilter: RpcMethod<[HexString], boolean>; + eth_getFilterChanges: RpcMethod<[HexString], (Log | Hash32)[]>; + eth_getFilterLogs: RpcMethod<[HexString], Log[]>; + eth_getLogs: RpcMethod<[Filter], Log[]>; + + // Subscription methods + eth_subscribe: RpcMethod<[string, object?], HexString>; + eth_unsubscribe: RpcMethod<[HexString], boolean>; + + // Network info methods + eth_chainId: RpcMethod<[], HexString>; + eth_syncing: RpcMethod<[], SyncingStatus>; + web3_clientVersion: RpcMethod<[], string>; }; - events: ['eth_subscription']; + events: ['eth_subscription', 'accountsChanged', 'chainChanged', 'connect', 'disconnect']; }; From 7e309e1dd6560a926e0c62ff4c833890b822927c Mon Sep 17 00:00:00 2001 From: Baptiste Marchand <75846779+baptiste-marchand@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:39:45 +0200 Subject: [PATCH 2/4] fix: enforce required properties in ScopeObject --- src/types/session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/session.ts b/src/types/session.ts index 3c45964..1e94359 100644 --- a/src/types/session.ts +++ b/src/types/session.ts @@ -21,8 +21,8 @@ type Json = */ export type ScopeObject = { references?: string[]; - methods?: string[]; - notifications?: string[]; + methods: string[]; + notifications: string[]; accounts?: CaipAccountId[]; }; From 2758a7936930d9fd04607645e2376b8b3730a4a0 Mon Sep 17 00:00:00 2001 From: Baptiste Marchand <75846779+baptiste-marchand@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:42:39 +0200 Subject: [PATCH 3/4] test: await expect.toThrow --- src/helpers/utils.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers/utils.test.ts b/src/helpers/utils.test.ts index 3ea02b9..e98817c 100644 --- a/src/helpers/utils.test.ts +++ b/src/helpers/utils.test.ts @@ -46,7 +46,7 @@ describe('utils', () => { }); it('should retry a function that never resolves until it succeeds', async () => { - expect( + await expect( async () => await withRetry(mockMultichainApiRequest(), { maxRetries: 2, requestTimeout: 100 }), ).rejects.toThrow('Timeout reached'); }); @@ -57,9 +57,9 @@ describe('utils', () => { }); it('should retry a throwing function until it succeeds', async () => { - expect(async () => await withRetry(mockThrowingFn(), { maxRetries: 2, requestTimeout: 100 })).rejects.toThrow( - 'error', - ); + await expect( + async () => await withRetry(mockThrowingFn(), { maxRetries: 2, requestTimeout: 100 }), + ).rejects.toThrow('error'); }); }); }); From 0646617dcdb95f6a320e5ada2c40456e460a3590 Mon Sep 17 00:00:00 2001 From: Baptiste Marchand <75846779+baptiste-marchand@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:42:50 +0200 Subject: [PATCH 4/4] test: update eth_call parameters in index tests --- tests/index.test-d.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/index.test-d.ts b/tests/index.test-d.ts index fd07a81..ce94dad 100644 --- a/tests/index.test-d.ts +++ b/tests/index.test-d.ts @@ -29,15 +29,19 @@ expectType<{ ); // Basic eth_call with correct scope and parameters -expectType( +expectType<`0x${string}`>( await client.invokeMethod({ scope: 'eip155:1', request: { method: 'eth_call', - params: { - to: '0x1234567890', - data: '0x1234567890', - }, + params: [ + { + from: '0x1234567890', + to: '0x1234567890', + data: '0x1234567890', + }, + 'latest', + ], }, }), );