viem 2.48.8 → 2.49.0

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/_cjs/actions/public/call.js +26 -6
  3. package/_cjs/actions/public/call.js.map +1 -1
  4. package/_cjs/actions/wallet/sendTransactionSync.js +2 -2
  5. package/_cjs/actions/wallet/sendTransactionSync.js.map +1 -1
  6. package/_cjs/chains/definitions/hyperEvm.js +6 -0
  7. package/_cjs/chains/definitions/hyperEvm.js.map +1 -1
  8. package/_cjs/chains/index.js.map +1 -1
  9. package/_cjs/clients/transports/http.js +19 -2
  10. package/_cjs/clients/transports/http.js.map +1 -1
  11. package/_cjs/errors/utils.js +17 -0
  12. package/_cjs/errors/utils.js.map +1 -1
  13. package/_cjs/errors/version.js +1 -1
  14. package/_cjs/index.js.map +1 -1
  15. package/_cjs/tempo/actions/index.js +2 -1
  16. package/_cjs/tempo/actions/index.js.map +1 -1
  17. package/_cjs/tempo/actions/wallet.js +24 -0
  18. package/_cjs/tempo/actions/wallet.js.map +1 -0
  19. package/_cjs/tempo/actions/zone.js +14 -9
  20. package/_cjs/tempo/actions/zone.js.map +1 -1
  21. package/_cjs/utils/buildRequest.js +12 -2
  22. package/_cjs/utils/buildRequest.js.map +1 -1
  23. package/_cjs/utils/ccip.js +17 -4
  24. package/_cjs/utils/ccip.js.map +1 -1
  25. package/_cjs/utils/promise/withRetry.js +23 -3
  26. package/_cjs/utils/promise/withRetry.js.map +1 -1
  27. package/_cjs/utils/promise/withTimeout.js +5 -2
  28. package/_cjs/utils/promise/withTimeout.js.map +1 -1
  29. package/_cjs/utils/rpc/http.js +5 -0
  30. package/_cjs/utils/rpc/http.js.map +1 -1
  31. package/_cjs/utils/wait.js +19 -2
  32. package/_cjs/utils/wait.js.map +1 -1
  33. package/_esm/actions/public/call.js +26 -6
  34. package/_esm/actions/public/call.js.map +1 -1
  35. package/_esm/actions/wallet/sendTransactionSync.js +2 -2
  36. package/_esm/actions/wallet/sendTransactionSync.js.map +1 -1
  37. package/_esm/chains/definitions/hyperEvm.js +6 -0
  38. package/_esm/chains/definitions/hyperEvm.js.map +1 -1
  39. package/_esm/chains/index.js +5 -1
  40. package/_esm/chains/index.js.map +1 -1
  41. package/_esm/clients/transports/http.js +19 -2
  42. package/_esm/clients/transports/http.js.map +1 -1
  43. package/_esm/errors/utils.js +15 -0
  44. package/_esm/errors/utils.js.map +1 -1
  45. package/_esm/errors/version.js +1 -1
  46. package/_esm/index.js.map +1 -1
  47. package/_esm/tempo/actions/index.js +1 -0
  48. package/_esm/tempo/actions/index.js.map +1 -1
  49. package/_esm/tempo/actions/wallet.js +87 -0
  50. package/_esm/tempo/actions/wallet.js.map +1 -0
  51. package/_esm/tempo/actions/zone.js +14 -9
  52. package/_esm/tempo/actions/zone.js.map +1 -1
  53. package/_esm/utils/buildRequest.js +12 -2
  54. package/_esm/utils/buildRequest.js.map +1 -1
  55. package/_esm/utils/ccip.js +17 -4
  56. package/_esm/utils/ccip.js.map +1 -1
  57. package/_esm/utils/promise/withRetry.js +23 -3
  58. package/_esm/utils/promise/withRetry.js.map +1 -1
  59. package/_esm/utils/promise/withTimeout.js +5 -2
  60. package/_esm/utils/promise/withTimeout.js.map +1 -1
  61. package/_esm/utils/rpc/http.js +5 -0
  62. package/_esm/utils/rpc/http.js.map +1 -1
  63. package/_esm/utils/wait.js +19 -2
  64. package/_esm/utils/wait.js.map +1 -1
  65. package/_types/actions/public/call.d.ts +4 -1
  66. package/_types/actions/public/call.d.ts.map +1 -1
  67. package/_types/chains/definitions/hyperEvm.d.ts +6 -9
  68. package/_types/chains/definitions/hyperEvm.d.ts.map +1 -1
  69. package/_types/chains/index.d.ts +5 -1
  70. package/_types/chains/index.d.ts.map +1 -1
  71. package/_types/clients/transports/http.d.ts.map +1 -1
  72. package/_types/errors/utils.d.ts +3 -0
  73. package/_types/errors/utils.d.ts.map +1 -1
  74. package/_types/errors/version.d.ts +1 -1
  75. package/_types/index.d.ts +1 -1
  76. package/_types/index.d.ts.map +1 -1
  77. package/_types/tempo/Capabilities.d.ts +2 -0
  78. package/_types/tempo/Capabilities.d.ts.map +1 -1
  79. package/_types/tempo/actions/index.d.ts +1 -0
  80. package/_types/tempo/actions/index.d.ts.map +1 -1
  81. package/_types/tempo/actions/wallet.d.ts +152 -0
  82. package/_types/tempo/actions/wallet.d.ts.map +1 -0
  83. package/_types/tempo/actions/zone.d.ts.map +1 -1
  84. package/_types/types/eip1193.d.ts +2 -0
  85. package/_types/types/eip1193.d.ts.map +1 -1
  86. package/_types/utils/buildRequest.d.ts +5 -3
  87. package/_types/utils/buildRequest.d.ts.map +1 -1
  88. package/_types/utils/ccip.d.ts +5 -3
  89. package/_types/utils/ccip.d.ts.map +1 -1
  90. package/_types/utils/promise/withRetry.d.ts +3 -2
  91. package/_types/utils/promise/withRetry.d.ts.map +1 -1
  92. package/_types/utils/promise/withTimeout.d.ts +1 -1
  93. package/_types/utils/promise/withTimeout.d.ts.map +1 -1
  94. package/_types/utils/rpc/http.d.ts +1 -1
  95. package/_types/utils/rpc/http.d.ts.map +1 -1
  96. package/_types/utils/wait.d.ts +3 -1
  97. package/_types/utils/wait.d.ts.map +1 -1
  98. package/actions/public/call.ts +59 -23
  99. package/actions/wallet/sendTransactionSync.ts +2 -2
  100. package/chains/definitions/hyperEvm.ts +6 -0
  101. package/chains/index.ts +2 -0
  102. package/clients/transports/http.ts +18 -2
  103. package/errors/utils.ts +19 -0
  104. package/errors/version.ts +1 -1
  105. package/index.ts +1 -0
  106. package/package.json +1 -1
  107. package/tempo/Capabilities.ts +2 -0
  108. package/tempo/actions/index.ts +1 -0
  109. package/tempo/actions/wallet.ts +210 -0
  110. package/tempo/actions/zone.ts +35 -11
  111. package/types/eip1193.ts +2 -0
  112. package/utils/buildRequest.ts +22 -6
  113. package/utils/ccip.ts +22 -4
  114. package/utils/promise/withRetry.ts +29 -2
  115. package/utils/promise/withTimeout.ts +6 -3
  116. package/utils/rpc/http.ts +7 -1
  117. package/utils/wait.ts +24 -2
@@ -11,4 +11,5 @@ export * as simulate from './simulate.js'
11
11
  export * as token from './token.js'
12
12
  export * as validator from './validator.js'
13
13
  export * as virtualAddress from './virtualAddress.js'
14
+ export * as wallet from './wallet.js'
14
15
  export * as zone from './zone.js'
@@ -0,0 +1,210 @@
1
+ import type { Address } from 'abitype'
2
+ import type { Client } from '../../clients/createClient.js'
3
+ import type { Transport } from '../../clients/transports/createTransport.js'
4
+ import type { ErrorType as CoreErrorType } from '../../errors/utils.js'
5
+ import type { Chain } from '../../types/chain.js'
6
+ import type { RequestErrorType } from '../../utils/buildRequest.js'
7
+ import type { TransactionReceipt } from '../Transaction.js'
8
+
9
+ /**
10
+ * Opens the wallet send flow with optional pre-filled send fields.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { createClient, custom } from 'viem'
15
+ * import { Actions } from 'viem/tempo'
16
+ *
17
+ * const client = createClient({
18
+ * transport: custom(window.ethereum),
19
+ * })
20
+ *
21
+ * const { receipt } = await Actions.wallet.send(client, {
22
+ * amount: '1.5',
23
+ * to: '0x...',
24
+ * token: '0x...',
25
+ * })
26
+ * ```
27
+ *
28
+ * @param client - Client.
29
+ * @param parameters - Parameters.
30
+ * @returns The submitted send receipt and chain ID.
31
+ */
32
+ export async function send<chain extends Chain | undefined>(
33
+ client: Client<Transport, chain>,
34
+ parameters: send.Parameters = {},
35
+ ): Promise<send.ReturnValue> {
36
+ return client.request<{
37
+ Method: 'wallet_send'
38
+ Parameters: [send.Parameters]
39
+ ReturnType: send.ReturnValue
40
+ }>(
41
+ {
42
+ method: 'wallet_send',
43
+ params: [parameters],
44
+ },
45
+ { retryCount: 0 },
46
+ )
47
+ }
48
+
49
+ export declare namespace send {
50
+ export type Parameters = {
51
+ /** Human-readable amount to pre-fill (for example, "1.5"). */
52
+ amount?: string | undefined
53
+ /**
54
+ * Fee payer override. `false` to disable the wallet's default fee payer,
55
+ * a URL string to use a custom fee payer service.
56
+ */
57
+ feePayer?: boolean | string | undefined
58
+ /**
59
+ * UTF-8 memo (max 32 bytes) to attach to the transfer. Wallet rejects
60
+ * the request if the selected token does not support memos (non-TIP-20).
61
+ */
62
+ memo?: string | undefined
63
+ /** Recipient address to pre-fill. */
64
+ to?: Address | undefined
65
+ /**
66
+ * Token to pre-fill, accepted as either a contract address or a curated
67
+ * tokenlist symbol (case-insensitive, for example `"pathUsd"`). Symbols
68
+ * are resolved against the curated tokenlist on the active chain. Omit
69
+ * to let the user choose.
70
+ */
71
+ token?: Address | string | undefined
72
+ }
73
+
74
+ export type ReturnValue = {
75
+ /** Chain ID the send was submitted to. */
76
+ chainId: number
77
+ /** Receipt of the submitted send. */
78
+ receipt: TransactionReceipt
79
+ }
80
+
81
+ export type ErrorType = RequestErrorType | CoreErrorType
82
+ }
83
+
84
+ /**
85
+ * Opens the wallet swap flow with optional pre-filled swap fields.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * import { createClient, custom } from 'viem'
90
+ * import { Actions } from 'viem/tempo'
91
+ *
92
+ * const client = createClient({
93
+ * transport: custom(window.ethereum),
94
+ * })
95
+ *
96
+ * const { receipt } = await Actions.wallet.swap(client, {
97
+ * amount: '1.5',
98
+ * token: '0x...',
99
+ * type: 'sell',
100
+ * })
101
+ * ```
102
+ *
103
+ * @param client - Client.
104
+ * @param parameters - Parameters.
105
+ * @returns The submitted swap receipt.
106
+ */
107
+ export async function swap<chain extends Chain | undefined>(
108
+ client: Client<Transport, chain>,
109
+ parameters: swap.Parameters = {},
110
+ ): Promise<swap.ReturnValue> {
111
+ return client.request<{
112
+ Method: 'wallet_swap'
113
+ Parameters: [swap.Parameters]
114
+ ReturnType: swap.ReturnValue
115
+ }>(
116
+ {
117
+ method: 'wallet_swap',
118
+ params: [parameters],
119
+ },
120
+ { retryCount: 0 },
121
+ )
122
+ }
123
+
124
+ export declare namespace swap {
125
+ export type Parameters = {
126
+ /** Human-readable amount to pre-fill (for example, "1.5"). */
127
+ amount?: string | undefined
128
+ /**
129
+ * Other side of the swap pair. For buys, this is the token to sell.
130
+ * For sells, this is the token to buy.
131
+ */
132
+ pairToken?: Address | undefined
133
+ /** Maximum allowed slippage as a decimal fraction, for example `0.05`. */
134
+ slippage?: number | undefined
135
+ /** Token to buy or sell. Omit to let the user choose. */
136
+ token?: Address | undefined
137
+ /** Whether the amount is an exact buy or sell amount. */
138
+ type?: 'buy' | 'sell' | undefined
139
+ }
140
+
141
+ export type ReturnValue = {
142
+ /** Receipt of the submitted swap. */
143
+ receipt: TransactionReceipt
144
+ }
145
+
146
+ export type ErrorType = RequestErrorType | CoreErrorType
147
+ }
148
+
149
+ /**
150
+ * Opens the wallet deposit flow with optional pre-filled deposit fields.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * import { createClient, custom } from 'viem'
155
+ * import { Actions } from 'viem/tempo'
156
+ *
157
+ * const client = createClient({
158
+ * transport: custom(window.ethereum),
159
+ * })
160
+ *
161
+ * const result = await Actions.wallet.deposit(client, {
162
+ * token: '0x...',
163
+ * value: '1.5',
164
+ * })
165
+ * ```
166
+ *
167
+ * @param client - Client.
168
+ * @param parameters - Parameters.
169
+ * @returns Receipts for onchain deposit operations, when applicable.
170
+ */
171
+ export async function deposit<chain extends Chain | undefined>(
172
+ client: Client<Transport, chain>,
173
+ parameters: deposit.Parameters = {},
174
+ ): Promise<deposit.ReturnValue> {
175
+ return client.request<{
176
+ Method: 'wallet_deposit'
177
+ Parameters: [deposit.Parameters]
178
+ ReturnType: deposit.ReturnValue
179
+ }>(
180
+ {
181
+ method: 'wallet_deposit',
182
+ params: [parameters],
183
+ },
184
+ { retryCount: 0 },
185
+ )
186
+ }
187
+
188
+ export declare namespace deposit {
189
+ export type Parameters = {
190
+ /** Deposit address to pre-fill. */
191
+ address?: Address | undefined
192
+ /** Source chain ID to pre-fill. */
193
+ chainId?: number | undefined
194
+ /** Human-readable account display name. */
195
+ displayName?: string | undefined
196
+ /** Token contract address to pre-fill. Omit to let the user choose. */
197
+ token?: Address | undefined
198
+ /** Human-readable amount to pre-fill (for example, "1.5"). */
199
+ value?: string | undefined
200
+ }
201
+
202
+ export type ReturnValue =
203
+ | {
204
+ /** Receipts of any onchain operations performed during the deposit. */
205
+ receipts?: readonly TransactionReceipt[] | undefined
206
+ }
207
+ | undefined
208
+
209
+ export type ErrorType = RequestErrorType | CoreErrorType
210
+ }
@@ -18,7 +18,6 @@ import { zeroHash } from '../../constants/bytes.js'
18
18
  import type { BaseErrorType } from '../../errors/base.js'
19
19
  import type { Chain } from '../../types/chain.js'
20
20
  import type { Compute } from '../../types/utils.js'
21
- import { encodeAbiParameters } from '../../utils/abi/encodeAbiParameters.js'
22
21
  import type { RequestErrorType } from '../../utils/buildRequest.js'
23
22
  import * as Abis from '../Abis.js'
24
23
  import * as Addresses from '../Addresses.js'
@@ -295,6 +294,8 @@ export async function encryptedDeposit<
295
294
  const encrypted = await encryptDepositPayload(
296
295
  { x: publicKey[0], yParity: publicKey[1] },
297
296
  recipient,
297
+ portalAddress,
298
+ keyIndex - 1n,
298
299
  parameters.memo,
299
300
  )
300
301
 
@@ -454,6 +455,8 @@ export async function encryptedDepositSync<
454
455
  const encrypted = await encryptDepositPayload(
455
456
  { x: publicKey[0], yParity: publicKey[1] },
456
457
  recipient,
458
+ portalAddress,
459
+ keyIndex - 1n,
457
460
  parameters.memo,
458
461
  )
459
462
 
@@ -1251,6 +1254,8 @@ export namespace signAuthorizationToken {
1251
1254
  async function encryptDepositPayload(
1252
1255
  publicKey: { x: Hex.Hex; yParity: number },
1253
1256
  recipient: Address,
1257
+ portalAddress: Address,
1258
+ keyIndex: bigint,
1254
1259
  memo: Hex.Hex = zeroHash,
1255
1260
  ): Promise<EncryptedPayload> {
1256
1261
  const sequencerPublicKey = PublicKey.from({
@@ -1260,6 +1265,7 @@ async function encryptDepositPayload(
1260
1265
 
1261
1266
  const { privateKey: ephemeralPrivateKey, publicKey: ephemeralPublicKey } =
1262
1267
  Secp256k1.createKeyPair()
1268
+ const compressedEphemeral = PublicKey.compress(ephemeralPublicKey)
1263
1269
 
1264
1270
  const sharedSecret = Secp256k1.getSharedSecret({
1265
1271
  privateKey: ephemeralPrivateKey,
@@ -1269,7 +1275,7 @@ async function encryptDepositPayload(
1269
1275
 
1270
1276
  const hkdfKey = await globalThis.crypto.subtle.importKey(
1271
1277
  'raw',
1272
- sharedSecret.buffer as ArrayBuffer,
1278
+ sharedSecret.slice(1),
1273
1279
  'HKDF',
1274
1280
  false,
1275
1281
  ['deriveKey'],
@@ -1278,8 +1284,12 @@ async function encryptDepositPayload(
1278
1284
  {
1279
1285
  name: 'HKDF',
1280
1286
  hash: 'SHA-256',
1281
- salt: new Uint8Array(12),
1282
- info: new TextEncoder().encode('ecies-aes-key'),
1287
+ salt: new TextEncoder().encode('ecies-aes-key'),
1288
+ info: buildDepositHkdfInfo(
1289
+ portalAddress,
1290
+ keyIndex,
1291
+ Hex.fromNumber(compressedEphemeral.x, { size: 32 }),
1292
+ ) as BufferSource,
1283
1293
  },
1284
1294
  hkdfKey,
1285
1295
  { name: 'AES-GCM', length: 256 },
@@ -1288,11 +1298,7 @@ async function encryptDepositPayload(
1288
1298
  )
1289
1299
 
1290
1300
  const nonce = Bytes.random(12)
1291
-
1292
- const plaintext = encodeAbiParameters(
1293
- [{ type: 'address' }, { type: 'bytes32' }],
1294
- [recipient, memo],
1295
- )
1301
+ const plaintext = buildDepositPlaintext(recipient, memo)
1296
1302
 
1297
1303
  const ciphertextWithTag = new Uint8Array(
1298
1304
  await globalThis.crypto.subtle.encrypt(
@@ -1305,8 +1311,6 @@ async function encryptDepositPayload(
1305
1311
  const ciphertext = ciphertextWithTag.slice(0, -16)
1306
1312
  const tag = ciphertextWithTag.slice(-16)
1307
1313
 
1308
- const compressedEphemeral = PublicKey.compress(ephemeralPublicKey)
1309
-
1310
1314
  return {
1311
1315
  ciphertext: Hex.fromBytes(ciphertext),
1312
1316
  ephemeralPubkeyX: Hex.fromNumber(compressedEphemeral.x, { size: 32 }),
@@ -1315,3 +1319,23 @@ async function encryptDepositPayload(
1315
1319
  tag: Hex.fromBytes(tag),
1316
1320
  }
1317
1321
  }
1322
+
1323
+ function buildDepositPlaintext(recipient: Address, memo: Hex.Hex): Bytes.Bytes {
1324
+ return Bytes.concat(
1325
+ Bytes.from(recipient),
1326
+ Bytes.from(memo),
1327
+ new Uint8Array(12),
1328
+ )
1329
+ }
1330
+
1331
+ function buildDepositHkdfInfo(
1332
+ portalAddress: Address,
1333
+ keyIndex: bigint,
1334
+ ephemeralPubkeyX: Hex.Hex,
1335
+ ): Bytes.Bytes {
1336
+ return Bytes.concat(
1337
+ Bytes.from(portalAddress),
1338
+ Bytes.fromNumber(keyIndex, { size: 32 }),
1339
+ Bytes.from(ephemeralPubkeyX),
1340
+ )
1341
+ }
package/types/eip1193.ts CHANGED
@@ -2140,6 +2140,8 @@ export type EIP1193RequestOptions = {
2140
2140
  retryDelay?: number | undefined
2141
2141
  /** The max number of times to retry. */
2142
2142
  retryCount?: number | undefined
2143
+ /** Abort signal to cancel the request. */
2144
+ signal?: AbortSignal | undefined
2143
2145
  /** Unique identifier for the request. */
2144
2146
  uid?: string | undefined
2145
2147
  }
@@ -66,7 +66,12 @@ import {
66
66
  WalletConnectSessionSettlementError,
67
67
  type WalletConnectSessionSettlementErrorType,
68
68
  } from '../errors/rpc.js'
69
- import type { ErrorType } from '../errors/utils.js'
69
+ import {
70
+ type AbortErrorType,
71
+ type ErrorType,
72
+ getAbortError,
73
+ isAbortError,
74
+ } from '../errors/utils.js'
70
75
  import type {
71
76
  EIP1193RequestFn,
72
77
  EIP1193RequestOptions,
@@ -113,18 +118,22 @@ export type RequestErrorType =
113
118
  | WalletConnectSessionSettlementErrorType
114
119
  | WebSocketRequestErrorType
115
120
  | WithRetryErrorType
121
+ | AbortErrorType
116
122
  | ErrorType
117
123
 
118
- export function buildRequest<request extends (args: any) => Promise<any>>(
119
- request: request,
120
- options: EIP1193RequestOptions = {},
121
- ): EIP1193RequestFn {
124
+ export function buildRequest<
125
+ request extends (
126
+ args: any,
127
+ options?: { signal?: AbortSignal | undefined } | undefined,
128
+ ) => Promise<any>,
129
+ >(request: request, options: EIP1193RequestOptions = {}): EIP1193RequestFn {
122
130
  return async (args, overrideOptions = {}) => {
123
131
  const {
124
132
  dedupe = false,
125
133
  methods,
126
134
  retryDelay = 150,
127
135
  retryCount = 3,
136
+ signal,
128
137
  uid,
129
138
  } = {
130
139
  ...options,
@@ -141,6 +150,8 @@ export function buildRequest<request extends (args: any) => Promise<any>>(
141
150
  method,
142
151
  })
143
152
 
153
+ if (signal?.aborted) throw getAbortError(signal)
154
+
144
155
  const requestId = dedupe
145
156
  ? hashString(`${uid}.${stringify(args)}`)
146
157
  : undefined
@@ -149,8 +160,11 @@ export function buildRequest<request extends (args: any) => Promise<any>>(
149
160
  withRetry(
150
161
  async () => {
151
162
  try {
152
- return await request(args)
163
+ return await request(args, signal ? { signal } : undefined)
153
164
  } catch (err_) {
165
+ if (signal?.aborted) throw getAbortError(signal)
166
+ if (isAbortError(err_)) throw err_
167
+
154
168
  const err = err_ as unknown as RpcError<
155
169
  RpcErrorCode | ProviderRpcErrorCode
156
170
  >
@@ -264,6 +278,7 @@ export function buildRequest<request extends (args: any) => Promise<any>>(
264
278
  return ~~(1 << count) * retryDelay
265
279
  },
266
280
  retryCount,
281
+ signal,
267
282
  shouldRetry: ({ error }) => shouldRetry(error),
268
283
  },
269
284
  ),
@@ -274,6 +289,7 @@ export function buildRequest<request extends (args: any) => Promise<any>>(
274
289
 
275
290
  /** @internal */
276
291
  export function shouldRetry(error: Error) {
292
+ if (isAbortError(error)) return false
277
293
  if ('code' in error && typeof error.code === 'number') {
278
294
  if (error.code === -1) return true // Unknown error
279
295
  if (error.code === LimitExceededRpcError.code) return true
package/utils/ccip.ts CHANGED
@@ -15,8 +15,9 @@ import {
15
15
  HttpRequestError,
16
16
  type HttpRequestErrorType,
17
17
  } from '../errors/request.js'
18
- import type { ErrorType } from '../errors/utils.js'
18
+ import { type ErrorType, getAbortError, isAbortError } from '../errors/utils.js'
19
19
  import type { Chain } from '../types/chain.js'
20
+ import type { EIP1193RequestOptions } from '../types/eip1193.js'
20
21
  import type { Hex } from '../types/misc.js'
21
22
  import { decodeErrorResult } from './abi/decodeErrorResult.js'
22
23
  import { encodeAbiParameters } from './abi/encodeAbiParameters.js'
@@ -65,8 +66,9 @@ export async function offchainLookup<chain extends Chain | undefined>(
65
66
  blockNumber,
66
67
  blockTag,
67
68
  data,
69
+ requestOptions,
68
70
  to,
69
- }: Pick<CallParameters, 'blockNumber' | 'blockTag'> & {
71
+ }: Pick<CallParameters, 'blockNumber' | 'blockTag' | 'requestOptions'> & {
70
72
  data: Hex
71
73
  to: Address
72
74
  },
@@ -90,9 +92,10 @@ export async function offchainLookup<chain extends Chain | undefined>(
90
92
  const result = urls.includes(localBatchGatewayUrl)
91
93
  ? await localBatchGatewayRequest({
92
94
  data: callData,
93
- ccipRequest: ccipRequest_,
95
+ ccipRequest: (parameters) =>
96
+ ccipRequest_({ ...parameters, requestOptions }),
94
97
  })
95
- : await ccipRequest_({ data: callData, sender, urls })
98
+ : await ccipRequest_({ data: callData, requestOptions, sender, urls })
96
99
 
97
100
  const { data: data_ } = await call(client, {
98
101
  blockNumber,
@@ -104,11 +107,16 @@ export async function offchainLookup<chain extends Chain | undefined>(
104
107
  [result, extraData],
105
108
  ),
106
109
  ]),
110
+ requestOptions,
107
111
  to,
108
112
  } as CallParameters)
109
113
 
110
114
  return data_!
111
115
  } catch (err) {
116
+ if (requestOptions?.signal?.aborted)
117
+ throw getAbortError(requestOptions.signal)
118
+ if (isAbortError(err)) throw err
119
+
112
120
  throw new OffchainLookupError({
113
121
  callbackSelector,
114
122
  cause: err as BaseError,
@@ -122,6 +130,7 @@ export async function offchainLookup<chain extends Chain | undefined>(
122
130
 
123
131
  export type CcipRequestParameters = {
124
132
  data: Hex
133
+ requestOptions?: EIP1193RequestOptions | undefined
125
134
  sender: Address
126
135
  urls: readonly string[]
127
136
  }
@@ -135,12 +144,16 @@ export type CcipRequestErrorType =
135
144
 
136
145
  export async function ccipRequest({
137
146
  data,
147
+ requestOptions,
138
148
  sender,
139
149
  urls,
140
150
  }: CcipRequestParameters): Promise<CcipRequestReturnType> {
141
151
  let error = new Error('An unknown error occurred.')
142
152
 
143
153
  for (let i = 0; i < urls.length; i++) {
154
+ if (requestOptions?.signal?.aborted)
155
+ throw getAbortError(requestOptions.signal)
156
+
144
157
  const url = urls[i]
145
158
  const method = url.includes('{data}') ? 'GET' : 'POST'
146
159
  const body = method === 'POST' ? { data, sender } : undefined
@@ -154,6 +167,7 @@ export async function ccipRequest({
154
167
  body: JSON.stringify(body),
155
168
  headers,
156
169
  method,
170
+ ...(requestOptions?.signal ? { signal: requestOptions.signal } : {}),
157
171
  },
158
172
  )
159
173
 
@@ -189,6 +203,10 @@ export async function ccipRequest({
189
203
 
190
204
  return result
191
205
  } catch (err) {
206
+ if (requestOptions?.signal?.aborted)
207
+ throw getAbortError(requestOptions.signal)
208
+ if (isAbortError(err)) throw err
209
+
192
210
  error = new HttpRequestError({
193
211
  body,
194
212
  details: (err as Error).message,
@@ -1,4 +1,8 @@
1
- import type { ErrorType } from '../../errors/utils.js'
1
+ import {
2
+ type ErrorType,
3
+ getAbortError,
4
+ isAbortError,
5
+ } from '../../errors/utils.js'
2
6
  import { wait } from '../wait.js'
3
7
 
4
8
  export type WithRetryParameters = {
@@ -19,6 +23,8 @@ export type WithRetryParameters = {
19
23
  error: Error
20
24
  }) => Promise<boolean> | boolean)
21
25
  | undefined
26
+ // AbortSignal to cancel retries.
27
+ signal?: AbortSignal | undefined
22
28
  }
23
29
 
24
30
  export type WithRetryErrorType = ErrorType
@@ -29,14 +35,27 @@ export function withRetry<data>(
29
35
  delay: delay_ = 100,
30
36
  retryCount = 2,
31
37
  shouldRetry = () => true,
38
+ signal,
32
39
  }: WithRetryParameters = {},
33
40
  ) {
34
41
  return new Promise<data>((resolve, reject) => {
35
42
  const attemptRetry = async ({ count = 0 } = {}) => {
43
+ if (signal?.aborted) {
44
+ reject(getAbortError(signal))
45
+ return
46
+ }
47
+
36
48
  const retry = async ({ error }: { error: Error }) => {
37
49
  const delay =
38
50
  typeof delay_ === 'function' ? delay_({ count, error }) : delay_
39
- if (delay) await wait(delay)
51
+ if (delay) {
52
+ try {
53
+ await wait(delay, { signal })
54
+ } catch (err) {
55
+ reject(err)
56
+ return
57
+ }
58
+ }
40
59
  attemptRetry({ count: count + 1 })
41
60
  }
42
61
 
@@ -44,6 +63,14 @@ export function withRetry<data>(
44
63
  const data = await fn()
45
64
  resolve(data)
46
65
  } catch (err) {
66
+ if (signal?.aborted) {
67
+ reject(getAbortError(signal))
68
+ return
69
+ }
70
+ if (isAbortError(err)) {
71
+ reject(err)
72
+ return
73
+ }
47
74
  if (
48
75
  count < retryCount &&
49
76
  (await shouldRetry({ count, error: err as Error }))
@@ -1,4 +1,4 @@
1
- import type { ErrorType } from '../../errors/utils.js'
1
+ import { type ErrorType, isAbortError } from '../../errors/utils.js'
2
2
 
3
3
  export type WithTimeoutErrorType = ErrorType
4
4
 
@@ -24,8 +24,8 @@ export function withTimeout<data>(
24
24
  return new Promise((resolve, reject) => {
25
25
  ;(async () => {
26
26
  let timeoutId!: NodeJS.Timeout
27
+ const controller = new AbortController()
27
28
  try {
28
- const controller = new AbortController()
29
29
  if (timeout > 0) {
30
30
  timeoutId = setTimeout(() => {
31
31
  if (signal) {
@@ -37,7 +37,10 @@ export function withTimeout<data>(
37
37
  }
38
38
  resolve(await fn({ signal: controller?.signal || null }))
39
39
  } catch (err) {
40
- if ((err as Error)?.name === 'AbortError') reject(errorInstance)
40
+ if (controller?.signal.aborted && isAbortError(err)) {
41
+ reject(errorInstance)
42
+ return
43
+ }
41
44
  reject(err)
42
45
  } finally {
43
46
  clearTimeout(timeoutId)
package/utils/rpc/http.ts CHANGED
@@ -4,7 +4,11 @@ import {
4
4
  TimeoutError,
5
5
  type TimeoutErrorType,
6
6
  } from '../../errors/request.js'
7
- import type { ErrorType } from '../../errors/utils.js'
7
+ import {
8
+ type ErrorType,
9
+ getAbortError,
10
+ isAbortError,
11
+ } from '../../errors/utils.js'
8
12
  import type { RpcRequest, RpcResponse } from '../../types/rpc.js'
9
13
  import type { MaybePromise } from '../../types/utils.js'
10
14
  import {
@@ -174,6 +178,8 @@ export function getHttpRpcClient(
174
178
 
175
179
  return data
176
180
  } catch (err) {
181
+ if (signal_?.aborted) throw getAbortError(signal_)
182
+ if (isAbortError(err)) throw err
177
183
  if (err instanceof HttpRequestError) throw err
178
184
  if (err instanceof TimeoutError) throw err
179
185
  throw new HttpRequestError({
package/utils/wait.ts CHANGED
@@ -1,3 +1,25 @@
1
- export async function wait(time: number) {
2
- return new Promise((res) => setTimeout(res, time))
1
+ import { getAbortError } from '../errors/utils.js'
2
+
3
+ export async function wait(
4
+ time: number,
5
+ { signal }: { signal?: AbortSignal | undefined } = {},
6
+ ) {
7
+ return new Promise<void>((resolve, reject) => {
8
+ if (signal?.aborted) {
9
+ reject(getAbortError(signal))
10
+ return
11
+ }
12
+
13
+ const cleanup = () => signal?.removeEventListener('abort', onAbort)
14
+ const timeout = setTimeout(() => {
15
+ cleanup()
16
+ resolve()
17
+ }, time)
18
+ const onAbort = () => {
19
+ clearTimeout(timeout)
20
+ cleanup()
21
+ reject(getAbortError(signal))
22
+ }
23
+ signal?.addEventListener('abort', onAbort, { once: true })
24
+ })
3
25
  }