@tetherto/wdk-wallet-evm 1.0.0-beta.10 → 1.0.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,493 +1,99 @@
1
1
  # @tetherto/wdk-wallet-evm
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/%40tetherto%2Fwdk-wallet-evm?style=flat-square)](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm)
4
+ [![npm downloads](https://img.shields.io/npm/dw/%40tetherto%2Fwdk-wallet-evm?style=flat-square)](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm)
5
+ [![license](https://img.shields.io/npm/l/%40tetherto%2Fwdk-wallet-evm?style=flat-square)](https://github.com/tetherto/wdk-wallet-evm/blob/main/LICENSE)
6
+ [![docs](https://img.shields.io/badge/docs-docs.wdk.tether.io-0A66C2?style=flat-square)](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm)
3
7
 
4
8
  **Note**: This package is currently in beta. Please test thoroughly in development environments before using in production.
5
9
 
6
10
  A simple and secure package to manage BIP-44 wallets for EVM-compatible blockchains. This package provides a clean API for creating, managing, and interacting with Ethereum-compatible wallets using BIP-39 seed phrases and EVM-specific derivation paths.
7
11
 
8
- ## 🔍 About WDK
12
+ ## About WDK
9
13
 
10
- This module is part of the [**WDK (Wallet Development Kit)**](https://wallet.tether.io/) project, which empowers developers to build secure, non-custodial wallets with unified blockchain access, stateless architecture, and complete user control.
14
+ This module is part of the [**WDK (Wallet Development Kit)**](https://docs.wdk.tether.io/) project, which empowers developers to build secure, non-custodial wallets with unified blockchain access, stateless architecture, and complete user control.
11
15
 
12
- For detailed documentation about the complete WDK ecosystem, visit [docs.wallet.tether.io](https://docs.wallet.tether.io).
16
+ For detailed documentation about the complete WDK ecosystem, visit [docs.wdk.tether.io](https://docs.wdk.tether.io).
13
17
 
14
- ## 🌟 Features
15
-
16
- - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases
17
- - **EVM Derivation Paths**: Support for BIP-44 standard derivation paths for Ethereum (m/44'/60')
18
- - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase
19
- - **Transaction Management**: Send transactions and get fee estimates with EIP-1559 support
20
- - **ERC20 Support**: Query native token and ERC20 token balances using smart contract interactions
21
- - **EIP-7702 Delegation**: Delegate EOAs to smart contracts, sign authorizations, and send type 4 transactions
22
-
23
- ## ⬇️ Installation
24
-
25
- To install the `@tetherto/wdk-wallet-evm` package, follow these instructions:
26
-
27
- You can install it using npm:
18
+ ## Installation
28
19
 
29
20
  ```bash
30
21
  npm install @tetherto/wdk-wallet-evm
31
22
  ```
32
23
 
33
- ## 🚀 Quick Start
34
-
35
- ### Importing from `@tetherto/wdk-wallet-evm`
36
-
37
- ### Creating a New Wallet
24
+ ## Quick Start
38
25
 
39
26
  ```javascript
40
- import WalletManagerEvm, { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm'
27
+ import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
41
28
 
42
- // Use a BIP-39 seed phrase (replace with your own secure phrase)
43
- const seedPhrase = 'test only example nut use this real life secret phrase must random'
29
+ const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
44
30
 
45
- // Create wallet manager with provider config
46
31
  const wallet = new WalletManagerEvm(seedPhrase, {
47
- // Option 1: Using RPC URL
48
- provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key', // or any EVM RPC endpoint
49
- transferMaxFee: 100000000000000 // Optional: Maximum fee in wei
50
- })
51
-
52
- // OR
53
-
54
- // Option 2: Using EIP-1193 provider (e.g., from browser wallet)
55
- const wallet2 = new WalletManagerEvm(seedPhrase, {
56
- provider: window.ethereum, // EIP-1193 provider
57
- transferMaxFee: 100000000000000 // Optional: Maximum fee in wei
32
+ provider: 'https://sepolia.drpc.org',
58
33
  })
59
34
 
60
- // Get a full access account
61
- const account = await wallet.getAccount(0)
62
-
63
- // Convert to a read-only account
64
- const readOnlyAccount = await account.toReadOnlyAccount()
65
- ```
66
-
67
- ### Managing Multiple Accounts
68
-
69
- ```javascript
70
- import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
71
-
72
- // Assume wallet is already created
73
- // Get the first account (index 0)
74
35
  const account = await wallet.getAccount(0)
75
36
  const address = await account.getAddress()
76
- console.log('Account 0 address:', address)
77
-
78
- // Get the second account (index 1)
79
- const account1 = await wallet.getAccount(1)
80
- const address1 = await account1.getAddress()
81
- console.log('Account 1 address:', address1)
82
-
83
- // Get account by custom derivation path
84
- // Full path will be m/44'/60'/0'/0/5
85
- const customAccount = await wallet.getAccountByPath("0'/0/5")
86
- const customAddress = await customAccount.getAddress()
87
- console.log('Custom account address:', customAddress)
88
-
89
- // Note: All addresses are checksummed Ethereum addresses (0x...)
90
- // All accounts inherit the provider configuration from the wallet manager
91
- ```
92
-
93
- ### Checking Balances
94
-
95
- #### Owned Account
96
-
97
- For accounts where you have the seed phrase and full access:
98
-
99
- ```javascript
100
- import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
101
-
102
- // Assume wallet and account are already created
103
- // Get native token balance (in wei)
104
- const balance = await account.getBalance()
105
- console.log('Native balance:', balance, 'wei') // 1 ETH = 1000000000000000000 wei
106
-
107
- // Get ERC20 token balance
108
- const tokenContract = '0x...'; // ERC20 contract address
109
- const tokenBalance = await account.getTokenBalance(tokenContract);
110
- console.log('Token balance:', tokenBalance);
111
-
112
- // Note: Provider is required for balance checks
113
- // Make sure wallet was created with a provider configuration
114
- ```
115
-
116
- #### Read-Only Account
117
-
118
- For addresses where you don't have the seed phrase:
119
-
120
- ```javascript
121
- import { WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm'
122
-
123
- // Create a read-only account
124
- const readOnlyAccount = new WalletAccountReadOnlyEvm('0x...', { // Ethereum address
125
- provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key' // Required for balance checks
126
- })
37
+ console.log('Address:', address)
127
38
 
128
- // Check native token balance
129
- const balance = await readOnlyAccount.getBalance()
130
- console.log('Native balance:', balance, 'wei')
131
-
132
- // Check ERC20 token balance using contract
133
- const tokenBalance = await readOnlyAccount.getTokenBalance('0x...') // ERC20 contract address
134
- console.log('Token balance:', tokenBalance)
135
-
136
- // Note: ERC20 balance checks use the standard balanceOf(address) function
137
- // Make sure the contract address is correct and implements the ERC20 standard
138
- ```
139
-
140
- ### Sending Transactions
141
-
142
- Send native tokens and estimate fees using `WalletAccountEvm`. Supports EIP-1559 fee model.
143
-
144
- ```javascript
145
- // Send native tokens
146
- // Modern EIP-1559 style transaction (recommended)
147
- const result = await account.sendTransaction({
148
- to: '0x...', // Recipient address
149
- value: 1000000000000000000n, // 1 ETH in wei
150
- maxFeePerGas: 30000000000, // Optional: max fee per gas (in wei)
151
- maxPriorityFeePerGas: 2000000000 // Optional: max priority fee per gas (in wei)
152
- })
153
- console.log('Transaction hash:', result.hash)
154
- console.log('Transaction fee:', result.fee, 'wei')
155
-
156
- // OR Legacy style transaction
157
- const legacyResult = await account.sendTransaction({
158
- to: '0x...',
159
- value: 1000000000000000000n,
160
- gasPrice: 20000000000n, // Optional: legacy gas price (in wei)
161
- gasLimit: 21000 // Optional: gas limit
162
- })
163
-
164
- // Get transaction fee estimate
165
- const quote = await account.quoteSendTransaction({
166
- to: '0x...',
167
- value: 1000000000000000000n
168
- });
169
- console.log('Estimated fee:', quote.fee, 'wei');
170
- ```
171
-
172
- ### Token Transfers
173
-
174
- Transfer ERC20 tokens and estimate fees using `WalletAccountEvm`. Uses standard ERC20 `transfer` function.
175
-
176
- ```javascript
177
- // Transfer ERC20 tokens
178
- const transferResult = await account.transfer({
179
- token: '0x...', // ERC20 contract address
180
- recipient: '0x...', // Recipient's address
181
- amount: 1000000n // Amount in token's base units (use BigInt for large numbers)
182
- });
183
- console.log('Transfer hash:', transferResult.hash);
184
- console.log('Transfer fee:', transferResult.fee, 'wei');
185
-
186
- // Quote token transfer fee
187
- const transferQuote = await account.quoteTransfer({
188
- token: '0x...', // ERC20 contract address
189
- recipient: '0x...', // Recipient's address
190
- amount: 1000000n // Amount in token's base units
191
- })
192
- console.log('Transfer fee estimate:', transferQuote.fee, 'wei')
193
- ```
194
-
195
- ### Message Signing and Verification
196
-
197
- Sign messages using `WalletAccountEvm` and verify signatures using `WalletAccountReadOnlyEvm`.
198
-
199
- ```javascript
200
- // Sign a message
201
- const message = 'Hello, Ethereum!'
202
- const signature = await account.sign(message)
203
- console.log('Signature:', signature)
204
-
205
- // Verify a signature (can use read-only account)
206
- const isValid = await readOnlyAccount.verify(message, signature)
207
- console.log('Signature valid:', isValid)
208
- ```
209
-
210
- ### Fee Management
211
-
212
- Retrieve current fee rates using `WalletManagerEvm`. Supports EIP-1559 fee model.
213
-
214
- ```javascript
215
- // Get current fee rates
216
- const feeRates = await wallet.getFeeRates();
217
- console.log('Normal fee rate:', feeRates.normal, 'wei'); // 1.1x base fee
218
- console.log('Fast fee rate:', feeRates.fast, 'wei'); // 2.0x base fee
219
- ```
220
-
221
- ### Memory Management
222
-
223
- Clear sensitive data from memory using `dispose` methods in `WalletAccountEvm` and `WalletManagerEvm`.
224
-
225
- ```javascript
226
- // Dispose wallet accounts to clear private keys from memory
227
- account.dispose()
228
-
229
- // Dispose entire wallet manager
230
39
  wallet.dispose()
231
40
  ```
232
41
 
233
- ### EIP-7702 Delegation
42
+ ## Key Capabilities
234
43
 
235
- Delegate an EOA to a smart contract using [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) type 4 transactions.
44
+ - **BIP-39 Seed Phrase Support**: Generate and validate mnemonic seed phrases
45
+ - **BIP-44 Derivation Paths**: Standard Ethereum derivation (m/44'/60')
46
+ - **Multi-Account Management**: Derive multiple accounts from a single seed phrase
47
+ - **EIP-1559 Transaction Support**: Modern fee estimation and transaction sending
48
+ - **ERC-20 Token Support**: Query balances and transfer tokens
49
+ - **Message Signing**: Sign and verify messages (EIP-191 and EIP-712)
50
+ - **Fee Estimation**: Real-time network fee rates with normal/fast tiers
51
+ - **Secure Memory Disposal**: Clear private keys from memory when done
236
52
 
237
- #### Delegate and Revoke
53
+ ## Compatibility
238
54
 
239
- ```javascript
240
- // Delegate the EOA to a smart contract
241
- const { hash, fee } = await account.delegate('0x...') // contract address
55
+ - **Ethereum Mainnet** and testnets (Sepolia)
56
+ - **Layer 2 Networks**: Arbitrum, Optimism, Base
57
+ - **Other EVM Chains**: Polygon, Avalanche C-Chain, and any EVM-compatible chain
242
58
 
243
- // Check delegation status
244
- const delegation = await account.getDelegation()
245
- console.log('Is delegated:', delegation.isDelegated)
246
- console.log('Delegate address:', delegation.delegateAddress)
59
+ ## Documentation
247
60
 
248
- // Revoke delegation
249
- await account.revokeDelegation()
250
- ```
61
+ | Topic | Description | Link |
62
+ |-------|-------------|------|
63
+ | Overview | Module overview and feature summary | [Wallet EVM Overview](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm) |
64
+ | Usage | End-to-end integration walkthrough | [Wallet EVM Usage](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/usage) |
65
+ | Configuration | Provider, fees, and network configuration | [Wallet EVM Configuration](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/configuration) |
66
+ | API Reference | Complete class and type reference | [Wallet EVM API Reference](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/api-reference) |
251
67
 
252
- #### Inline Delegation with Authorization List
68
+ ## Examples
253
69
 
254
- Sign an authorization and include it in a transaction. This sets the delegation and executes the transaction body in a single type 4 tx.
70
+ | Example | Description |
71
+ |---------|-------------|
72
+ | [Create Wallet](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/create-wallet.ts) | Initialize a wallet manager and derive accounts from a seed phrase |
73
+ | [Manage Accounts](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/manage-accounts.ts) | Work with multiple accounts and custom derivation paths |
74
+ | [Check Balances](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/check-balances.ts) | Query native token and ERC-20 balances for owned accounts |
75
+ | [Read-Only Account](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/read-only-account.ts) | Monitor balances for any address without a private key |
76
+ | [Send Transaction](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/send-transaction.ts) | Estimate fees and send native token transactions (EIP-1559 + legacy) |
77
+ | [Token Transfer](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/token-transfer.ts) | Transfer ERC-20 tokens and estimate transfer fees |
78
+ | [Sign & Verify Message](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/sign-verify-message.ts) | Sign messages and verify signatures |
79
+ | [Fee Management](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/fee-management.ts) | Retrieve current network fee rates |
80
+ | [Memory Management](https://github.com/tetherto/wdk-examples/blob/main/wallet-evm/memory-management.ts) | Securely dispose wallets and clear private keys from memory |
255
81
 
256
- ```javascript
257
- // Sign an authorization for the delegate contract
258
- const auth = await account.signAuthorization({
259
- address: '0x...' // contract address
260
- })
261
-
262
- // Send a type 4 transaction with the authorization list
263
- const result = await account.sendTransaction({
264
- type: 4,
265
- to: '0x...',
266
- value: 0,
267
- data: '0x...',
268
- authorizationList: [auth]
269
- })
270
- ```
271
-
272
- #### Check Delegation (Read-Only)
273
-
274
- ```javascript
275
- import { WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm'
276
-
277
- const readOnly = new WalletAccountReadOnlyEvm('0x...', {
278
- provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key'
279
- })
280
-
281
- const delegation = await readOnly.getDelegation()
282
- console.log('Is delegated:', delegation.isDelegated)
283
- ```
284
-
285
- ## 📚 API Reference
286
-
287
- ### Table of Contents
288
-
289
- ### Table of Contents
290
-
291
- | Class | Description | Methods |
292
- |-------|-------------|---------|
293
- | [WalletManagerEvm](#walletmanagerevm) | Main class for managing EVM wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) |
294
- | [WalletAccountEvm](#walletaccountevm) | Individual EVM wallet account implementation. Extends `WalletAccountReadOnlyEvm` and implements `IWalletAccount` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) |
295
- | [WalletAccountReadOnlyEvm](#walletaccountreadonlyevm) | Read-only EVM wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-2), [Methods](#methods-2) |
296
-
297
- ### WalletManagerEvm
298
-
299
- The main class for managing EVM wallets.
300
- Extends `WalletManager` from `@tetherto/wdk-wallet`.
301
-
302
- #### Constructor
303
-
304
- ```javascript
305
- new WalletManagerEvm(seed, config)
306
- ```
307
-
308
- **Parameters:**
309
- - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes
310
- - `config` (object, optional): Configuration object
311
- - `provider` (string | Eip1193Provider): RPC endpoint URL or EIP-1193 provider instance
312
- - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations (in wei)
313
-
314
- **Example:**
315
- ```javascript
316
- const wallet = new WalletManagerEvm(seedPhrase, {
317
- provider: 'https://eth-mainnet.g.alchemy.com/v2/your-api-key',
318
- transferMaxFee: '100000000000000' // Maximum fee in wei
319
- })
320
- ```
321
-
322
- #### Methods
323
-
324
- | Method | Description | Returns |
325
- |--------|-------------|---------|
326
- | `getAccount(index)` | Returns a wallet account at the specified index | `Promise<WalletAccountEvm>` |
327
- | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise<WalletAccountEvm>` |
328
- | `getFeeRates()` | Returns current fee rates for transactions | `Promise<{normal: bigint, fast: bigint}>` |
329
- | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` |
330
-
331
- ### WalletAccountEvm
332
-
333
- Represents an individual wallet account. Implements `IWalletAccount` from `@tetherto/wdk-wallet`.
334
-
335
- #### Constructor
336
-
337
- ```javascript
338
- new WalletAccountEvm(seed, path, config)
339
- ```
340
-
341
- **Parameters:**
342
- - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes
343
- - `path` (string): BIP-44 derivation path (e.g., "0'/0/0")
344
- - `config` (object, optional): Configuration object
345
- - `provider` (string | Eip1193Provider): RPC endpoint URL or EIP-1193 provider instance
346
- - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations (in wei)
347
-
348
- #### Methods
349
-
350
- | Method | Description | Returns |
351
- |--------|-------------|---------|
352
- | `getAddress()` | Returns the account's address | `Promise<string>` |
353
- | `sign(message)` | Signs a message using the account's private key | `Promise<string>` |
354
- | `signTypedData(typedData)` | Signs typed data according to EIP-712 | `Promise<string>` |
355
- | `verify(message, signature)` | Verifies a message signature | `Promise<boolean>` |
356
- | `verifyTypedData(typedData, signature)` | Verifies a typed data signature | `Promise<boolean>` |
357
- | `sendTransaction(tx)` | Sends an EVM transaction | `Promise<{hash: string, fee: bigint}>` |
358
- | `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction | `Promise<{fee: bigint}>` |
359
- | `transfer(options)` | Transfers ERC20 tokens to another address | `Promise<{hash: string, fee: bigint}>` |
360
- | `quoteTransfer(options)` | Estimates the fee for an ERC20 transfer | `Promise<{fee: bigint}>` |
361
- | `getBalance()` | Returns the native token balance (in wei) | `Promise<bigint>` |
362
- | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise<bigint>` |
363
- | `signAuthorization(auth)` | Signs an ERC-7702 authorization tuple | `Promise<Authorization>` |
364
- | `delegate(delegateAddress)` | Delegates this EOA to a smart contract via a type 4 transaction | `Promise<{hash: string, fee: bigint}>` |
365
- | `revokeDelegation()` | Revokes any active ERC-7702 delegation | `Promise<{hash: string, fee: bigint}>` |
366
- | `getDelegation()` | Checks if the account has an active ERC-7702 delegation | `Promise<{isDelegated: boolean, delegateAddress: string \| null}>` |
367
- | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` |
82
+ > For detailed walkthroughs, see the [Usage Guide](https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/usage).
83
+ > See all runnable examples in the [wdk-examples](https://github.com/tetherto/wdk-examples) repository.
368
84
 
369
- ##### `sendTransaction(tx)`
370
- Sends an EVM transaction.
85
+ ## Community
371
86
 
372
- **Parameters:**
373
- - `tx` (object): The transaction object
374
- - `to` (string): Recipient address
375
- - `value` (number | bigint): Amount in wei
376
- - `data` (string, optional): Transaction data in hex format
377
- - `gasLimit` (number | bigint, optional): Maximum gas units
378
- - `gasPrice` (number | bigint, optional): Legacy gas price in wei
379
- - `maxFeePerGas` (number | bigint, optional): EIP-1559 max fee per gas in wei
380
- - `maxPriorityFeePerGas` (number | bigint, optional): EIP-1559 max priority fee per gas in wei
381
- - `type` (number, optional): Transaction type (e.g. 4 for ERC-7702)
382
- - `nonce` (number, optional): Transaction nonce
383
- - `authorizationList` (Authorization[], optional): Signed ERC-7702 authorizations for type 4 transactions
87
+ Join the [WDK Discord](https://discord.gg/arYXDhHB2w) to connect with other developers.
384
88
 
385
- **Returns:** `Promise<{hash: string, fee: bigint}>` - Object containing hash and fee (in wei)
89
+ ## Support
386
90
 
387
- > When `authorizationList` is present, the method waits for the transaction to be mined and returns the actual fee. Otherwise, it returns after broadcast with an estimated fee.
91
+ For support, please [open an issue](https://github.com/tetherto/wdk-wallet-evm/issues) on GitHub or reach out via [email](mailto:wallet-info@tether.io).
388
92
 
389
- #### Properties
390
-
391
- | Property | Type | Description |
392
- |----------|------|-------------|
393
- | `index` | `number` | The derivation path's index of this account |
394
- | `path` | `string` | The full derivation path of this account |
395
- | `keyPair` | `object` | The account's key pair (⚠️ Contains sensitive data) |
396
-
397
- ⚠️ **Security Note**: The `keyPair` property contains sensitive cryptographic material. Never log, display, or expose the private key.
398
-
399
- ### WalletAccountReadOnlyEvm
400
-
401
- Represents a read-only wallet account.
402
-
403
- #### Constructor
404
-
405
- ```javascript
406
- new WalletAccountReadOnlyEvm(address, config)
407
- ```
408
-
409
- **Parameters:**
410
- - `address` (string): The account's address
411
- - `config` (object, optional): Configuration object
412
- - `provider` (string | Eip1193Provider): RPC endpoint URL or EIP-1193 provider instance
413
-
414
- #### Methods
415
-
416
- | Method | Description | Returns |
417
- |--------|-------------|---------|
418
- | `getBalance()` | Returns the native token balance (in wei) | `Promise<bigint>` |
419
- | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise<bigint>` |
420
- | `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction | `Promise<{fee: bigint}>` |
421
- | `quoteTransfer(options)` | Estimates the fee for an ERC20 transfer | `Promise<{fee: bigint}>` |
422
- | `verify(message, signature)` | Verifies a message signature | `Promise<boolean>` |
423
- | `verifyTypedData(typedData, signature)` | Verifies a typed data signature | `Promise<boolean>` |
424
- | `getDelegation()` | Checks if the account has an active ERC-7702 delegation | `Promise<{isDelegated: boolean, delegateAddress: string \| null}>` |
425
-
426
- #### Properties
427
-
428
- | Property | Type | Description |
429
- |----------|------|-------------|
430
- | `address` | `string` | The account's address |
431
-
432
- ## 🌐 Supported Networks
433
-
434
- This package works with any EVM-compatible blockchain, including:
435
-
436
- - **Ethereum Mainnet**
437
- - **Ethereum Testnets** (Sepolia, etc.)
438
- - **Layer 2 Networks** (Arbitrum, Optimism, etc.)
439
- - **Other EVM Chains** (Polygon, Avalanche C-Chain, etc.)
440
-
441
- ## 🔒 Security Considerations
442
-
443
- - **Seed Phrase Security**: Always store your seed phrase securely and never share it
444
- - **Private Key Management**: The package handles private keys internally with memory safety features
445
- - **Provider Security**: Use trusted RPC endpoints and consider running your own node for production
446
- - **Transaction Validation**: Always validate transaction details before signing
447
- - **Memory Cleanup**: Use the `dispose()` method to clear private keys from memory when done
448
- - **Fee Limits**: Set `transferMaxFee` in config to prevent excessive transaction fees
449
- - **Gas Estimation**: Always estimate gas before sending transactions
450
- - **EIP-1559**: Consider using EIP-1559 fee model for better gas price estimation
451
- - **Contract Interactions**: Verify contract addresses and token decimals before transfers
452
-
453
- ## 🛠️ Development
454
-
455
- ### Building
456
-
457
- ```bash
458
- # Install dependencies
459
- npm install
460
-
461
- # Build TypeScript definitions
462
- npm run build:types
463
-
464
- # Lint code
465
- npm run lint
466
-
467
- # Fix linting issues
468
- npm run lint:fix
469
- ```
470
-
471
- ### Testing
472
-
473
- ```bash
474
- # Run tests
475
- npm test
476
-
477
- # Run tests with coverage
478
- npm run test:coverage
479
- ```
480
-
481
- ## 📜 License
482
-
483
- This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
484
-
485
- ## 🤝 Contributing
93
+ ## Contributing
486
94
 
487
95
  Contributions are welcome! Please feel free to submit a Pull Request.
488
96
 
489
- ## 🆘 Support
97
+ ## License
490
98
 
491
- For support, please open an issue on the GitHub repository.
492
-
493
- ---
99
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
@@ -4,6 +4,7 @@ require('@nomicfoundation/hardhat-ethers')
4
4
  module.exports = {
5
5
  networks: {
6
6
  hardhat: {
7
+ chainId: 1,
7
8
  hardfork: 'prague',
8
9
  accounts: {
9
10
  mnemonic: 'anger burst story spy face pattern whale quit delay fiction ball solve',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tetherto/wdk-wallet-evm",
3
- "version": "1.0.0-beta.10",
3
+ "version": "1.0.0-beta.12",
4
4
  "description": "A simple package to manage BIP-32 wallets for evm blockchains.",
5
5
  "keywords": [
6
6
  "wdk",
@@ -30,7 +30,8 @@
30
30
  "@noble/curves": "1.9.2",
31
31
  "@noble/hashes": "1.8.0",
32
32
  "@noble/secp256k1": "2.2.3",
33
- "@tetherto/wdk-wallet": "1.0.0-beta.7",
33
+ "@tetherto/wdk-failover-provider": "1.0.0-beta.2",
34
+ "@tetherto/wdk-wallet": "1.0.0-beta.8",
34
35
  "bare-node-runtime": "^1.1.4",
35
36
  "bip39": "3.1.0",
36
37
  "ethers": "6.14.4",
@@ -45,9 +46,14 @@
45
46
  "typescript": "5.8.3"
46
47
  },
47
48
  "overrides": {
49
+ "brace-expansion": "1.1.13",
48
50
  "cookie": "1.0.2",
51
+ "immutable": "4.3.8",
52
+ "picomatch@2": "2.3.2",
53
+ "serialize-javascript": ">=7.0.5",
49
54
  "tmp": "0.2.5",
50
- "undici": "7.21.0"
55
+ "undici": ">=7.24.0",
56
+ "uuid": "14.0.0"
51
57
  },
52
58
  "exports": {
53
59
  ".": {
@@ -147,6 +147,19 @@ export default class WalletAccountEvm extends WalletAccountReadOnlyEvm {
147
147
  return await this._account.signTypedData(domain, types, message)
148
148
  }
149
149
 
150
+ /**
151
+ * Signs a transaction.
152
+ *
153
+ * @param {EvmTransaction} tx - The transaction to sign.
154
+ * @returns {Promise<string>} The signed transaction as a hex string.
155
+ */
156
+ async signTransaction (tx) {
157
+ return await this._account.signTransaction({
158
+ from: await this.getAddress(),
159
+ ...tx
160
+ })
161
+ }
162
+
150
163
  /**
151
164
  * Sends a transaction.
152
165
  *
@@ -234,9 +247,11 @@ export default class WalletAccountEvm extends WalletAccountReadOnlyEvm {
234
247
  * @returns {Promise<WalletAccountReadOnlyEvm>} The read-only account.
235
248
  */
236
249
  async toReadOnlyAccount () {
237
- const readOnlyAccount = new WalletAccountReadOnlyEvm(this._account.address, this._config)
250
+ if (!this._evmReadOnlyAccount) {
251
+ this._evmReadOnlyAccount = new WalletAccountReadOnlyEvm(this._account.address, this._config)
252
+ }
238
253
 
239
- return readOnlyAccount
254
+ return this._evmReadOnlyAccount
240
255
  }
241
256
 
242
257
  /**
@@ -16,10 +16,12 @@
16
16
 
17
17
  import { WalletAccountReadOnly } from '@tetherto/wdk-wallet'
18
18
 
19
- import { BrowserProvider, Contract, Interface, JsonRpcProvider, Signature, toQuantity, verifyMessage, verifyTypedData } from 'ethers'
19
+ import { BrowserProvider, Contract, Interface, JsonRpcProvider, Network, Signature, toQuantity, verifyMessage, verifyTypedData } from 'ethers'
20
20
 
21
21
  import { multicall } from './multicall.js'
22
22
 
23
+ import FailoverProvider from '@tetherto/wdk-failover-provider'
24
+
23
25
  /** @typedef {import('ethers').Provider} Provider */
24
26
  /** @typedef {import('ethers').Eip1193Provider} Eip1193Provider */
25
27
  /** @typedef {import('ethers').TypedDataDomain} TypedDataDomain */
@@ -54,6 +56,7 @@ import { multicall } from './multicall.js'
54
56
  * @property {number | bigint} [maxPriorityFeePerGas] - The price (in wei) per unit of gas this transaction will allow in addition to the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) block's base fee to bribe miners into giving this transaction priority. This is included in the maxFeePerGas, so this will not affect the total maximum cost set with maxFeePerGas.
55
57
  * @property {number} [type] - The transaction type (e.g. 4 for ERC-7702).
56
58
  * @property {number} [nonce] - The transaction nonce.
59
+ * @property {number | bigint} [chainId] - The chain ID of the network.
57
60
  * @property {AuthorizationLike[]} [authorizationList] - An optional list of ERC-7702 signed authorizations for type 4 transactions.
58
61
  */
59
62
 
@@ -67,7 +70,9 @@ import { multicall } from './multicall.js'
67
70
 
68
71
  /**
69
72
  * @typedef {Object} EvmWalletConfig
70
- * @property {string | Eip1193Provider} [provider] - The url of the rpc provider, or an instance of a class that implements eip-1193.
73
+ * @property {string | Eip1193Provider | Array<string | Eip1193Provider>} [provider] - The url of the rpc provider, or an instance of a class that implements eip-1193. It's also possible to provide an array of urls or EIP 1193 providers instead. In such case, connection errors will cause the wallet to automatically fallback on the next provider in the list.
74
+ * @property {number} [retries] - If set and if 'provider' is a list of urls or EIP 1193 providers, the number of additional retry attempts after the initial call fails. Total attempts = `1 + retries`. For example, `retries: 3` with 4 providers will try each provider once before throwing. If `retries` exceeds the number of providers, the failover will loop back and retry already-failed providers in round-robin order. Default: 3.
75
+ * @property {number} [chainId] - The chain ID of the network. When provided, skips automatic chain ID detection from the provider.
71
76
  * @property {number | bigint} [transferMaxFee] - The maximum fee amount for transfer operations.
72
77
  */
73
78
 
@@ -93,18 +98,36 @@ export default class WalletAccountReadOnlyEvm extends WalletAccountReadOnly {
93
98
  */
94
99
  this._config = config
95
100
 
96
- const { provider } = config
97
-
98
- if (provider) {
99
- /**
100
- * An ethers provider to interact with a node of the blockchain.
101
- *
102
- * @protected
103
- * @type {Provider | undefined}
104
- */
105
- this._provider = typeof provider === 'string'
106
- ? new JsonRpcProvider(provider)
107
- : new BrowserProvider(provider)
101
+ /**
102
+ * An ethers provider to interact with a node of the blockchain.
103
+ *
104
+ * @protected
105
+ * @type {Provider | undefined}
106
+ */
107
+ this._provider = undefined
108
+
109
+ const { provider, retries = 3 } = config
110
+ const network = config.chainId ? Network.from(config.chainId) : undefined
111
+ const providerOpts = config.chainId ? { staticNetwork: true } : undefined
112
+
113
+ if (Array.isArray(provider)) {
114
+ if (provider.length > 0) {
115
+ const failoverProvider = new FailoverProvider({ retries })
116
+
117
+ for (const entry of provider) {
118
+ const option = typeof entry === 'string'
119
+ ? new JsonRpcProvider(entry, network, providerOpts)
120
+ : new BrowserProvider(entry)
121
+ failoverProvider.addProvider(option)
122
+ }
123
+
124
+ this._provider = failoverProvider.initialize()
125
+ }
126
+ } else if (provider) {
127
+ this._provider =
128
+ typeof provider === 'string'
129
+ ? new JsonRpcProvider(provider, network, providerOpts)
130
+ : new BrowserProvider(provider)
108
131
  }
109
132
  }
110
133
 
@@ -18,6 +18,8 @@ import WalletManager from '@tetherto/wdk-wallet'
18
18
 
19
19
  import { BrowserProvider, JsonRpcProvider } from 'ethers'
20
20
 
21
+ import FailoverProvider from '@tetherto/wdk-failover-provider'
22
+
21
23
  import WalletAccountEvm from './wallet-account-evm.js'
22
24
 
23
25
  /** @typedef {import('ethers').Provider} Provider */
@@ -60,18 +62,34 @@ export default class WalletManagerEvm extends WalletManager {
60
62
  */
61
63
  this._config = config
62
64
 
63
- const { provider } = config
64
-
65
- if (provider) {
66
- /**
67
- * An ethers provider to interact with a node of the blockchain.
68
- *
69
- * @protected
70
- * @type {Provider | undefined}
71
- */
72
- this._provider = typeof provider === 'string'
73
- ? new JsonRpcProvider(provider)
74
- : new BrowserProvider(provider)
65
+ /**
66
+ * An ethers provider to interact with a node of the blockchain.
67
+ *
68
+ * @protected
69
+ * @type {Provider | undefined}
70
+ */
71
+ this._provider = undefined
72
+
73
+ const { provider, retries = 3 } = config
74
+
75
+ if (Array.isArray(provider)) {
76
+ if (provider.length > 0) {
77
+ const failoverProvider = new FailoverProvider({ retries })
78
+
79
+ for (const entry of provider) {
80
+ const option = typeof entry === 'string'
81
+ ? new JsonRpcProvider(entry)
82
+ : new BrowserProvider(entry)
83
+ failoverProvider.addProvider(option)
84
+ }
85
+
86
+ this._provider = failoverProvider.initialize()
87
+ }
88
+ } else if (provider) {
89
+ this._provider =
90
+ typeof provider === 'string'
91
+ ? new JsonRpcProvider(provider)
92
+ : new BrowserProvider(provider)
75
93
  }
76
94
  }
77
95
 
@@ -54,6 +54,13 @@ export default class WalletAccountEvm extends WalletAccountReadOnlyEvm implement
54
54
  * @returns {Promise<string>} The typed data signature.
55
55
  */
56
56
  signTypedData({ domain, types, message }: TypedData): Promise<string>;
57
+ /**
58
+ * Signs a transaction.
59
+ *
60
+ * @param {EvmTransaction} tx - The transaction to sign.
61
+ * @returns {Promise<string>} The signed transaction as a hex string.
62
+ */
63
+ signTransaction(tx: EvmTransaction): Promise<string>;
57
64
  /**
58
65
  * Sends a transaction.
59
66
  *
@@ -176,6 +176,10 @@ export type EvmTransaction = {
176
176
  * - The transaction nonce.
177
177
  */
178
178
  nonce?: number;
179
+ /**
180
+ * - The chain ID of the network.
181
+ */
182
+ chainId?: number | bigint;
179
183
  /**
180
184
  * - An optional list of ERC-7702 signed authorizations for type 4 transactions.
181
185
  */
@@ -201,9 +205,17 @@ export type EvmTransferOptions = {
201
205
  };
202
206
  export type EvmWalletConfig = {
203
207
  /**
204
- * - The url of the rpc provider, or an instance of a class that implements eip-1193.
208
+ * - The url of the rpc provider, or an instance of a class that implements eip-1193. It's also possible to provide an array of urls or EIP 1193 providers instead. In such case, connection errors will cause the wallet to automatically fallback on the next provider in the list.
209
+ */
210
+ provider?: string | Eip1193Provider | Array<string | Eip1193Provider>;
211
+ /**
212
+ * - If set and if 'provider' is a list of urls or EIP 1193 providers, the number of additional retry attempts after the initial call fails. Total attempts = `1 + retries`. For example, `retries: 3` with 4 providers will try each provider once before throwing. If `retries` exceeds the number of providers, the failover will loop back and retry already-failed providers in round-robin order. Default: 3.
213
+ */
214
+ retries?: number;
215
+ /**
216
+ * - The chain ID of the network. When provided, skips automatic chain ID detection from the provider.
205
217
  */
206
- provider?: string | Eip1193Provider;
218
+ chainId?: number;
207
219
  /**
208
220
  * - The maximum fee amount for transfer operations.
209
221
  */