viem 2.48.11 → 2.49.2
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/CHANGELOG.md +33 -0
- package/_cjs/actions/public/call.js +26 -6
- package/_cjs/actions/public/call.js.map +1 -1
- package/_cjs/clients/transports/http.js +19 -2
- package/_cjs/clients/transports/http.js.map +1 -1
- package/_cjs/errors/utils.js +17 -0
- package/_cjs/errors/utils.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/errors/version.js.map +1 -1
- package/_cjs/index.js.map +1 -1
- package/_cjs/tempo/Formatters.js +11 -3
- package/_cjs/tempo/Formatters.js.map +1 -1
- package/_cjs/tempo/actions/wallet.js +3 -3
- package/_cjs/tempo/actions/wallet.js.map +1 -1
- package/_cjs/utils/buildRequest.js +12 -2
- package/_cjs/utils/buildRequest.js.map +1 -1
- package/_cjs/utils/ccip.js +17 -4
- package/_cjs/utils/ccip.js.map +1 -1
- package/_cjs/utils/promise/withRetry.js +23 -3
- package/_cjs/utils/promise/withRetry.js.map +1 -1
- package/_cjs/utils/promise/withTimeout.js +5 -2
- package/_cjs/utils/promise/withTimeout.js.map +1 -1
- package/_cjs/utils/rpc/http.js +5 -0
- package/_cjs/utils/rpc/http.js.map +1 -1
- package/_cjs/utils/wait.js +19 -2
- package/_cjs/utils/wait.js.map +1 -1
- package/_esm/actions/public/call.js +26 -6
- package/_esm/actions/public/call.js.map +1 -1
- package/_esm/clients/transports/http.js +19 -2
- package/_esm/clients/transports/http.js.map +1 -1
- package/_esm/errors/utils.js +15 -0
- package/_esm/errors/utils.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/errors/version.js.map +1 -1
- package/_esm/index.js.map +1 -1
- package/_esm/tempo/Formatters.js +21 -4
- package/_esm/tempo/Formatters.js.map +1 -1
- package/_esm/tempo/actions/wallet.js +19 -6
- package/_esm/tempo/actions/wallet.js.map +1 -1
- package/_esm/utils/buildRequest.js +12 -2
- package/_esm/utils/buildRequest.js.map +1 -1
- package/_esm/utils/ccip.js +17 -4
- package/_esm/utils/ccip.js.map +1 -1
- package/_esm/utils/promise/withRetry.js +23 -3
- package/_esm/utils/promise/withRetry.js.map +1 -1
- package/_esm/utils/promise/withTimeout.js +5 -2
- package/_esm/utils/promise/withTimeout.js.map +1 -1
- package/_esm/utils/rpc/http.js +5 -0
- package/_esm/utils/rpc/http.js.map +1 -1
- package/_esm/utils/wait.js +19 -2
- package/_esm/utils/wait.js.map +1 -1
- package/_types/actions/public/call.d.ts +4 -1
- package/_types/actions/public/call.d.ts.map +1 -1
- package/_types/clients/transports/http.d.ts.map +1 -1
- package/_types/errors/utils.d.ts +3 -0
- package/_types/errors/utils.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/errors/version.d.ts.map +1 -1
- package/_types/index.d.ts +1 -1
- package/_types/index.d.ts.map +1 -1
- package/_types/tempo/Formatters.d.ts.map +1 -1
- package/_types/tempo/actions/wallet.d.ts +77 -15
- package/_types/tempo/actions/wallet.d.ts.map +1 -1
- package/_types/types/eip1193.d.ts +2 -0
- package/_types/types/eip1193.d.ts.map +1 -1
- package/_types/utils/buildRequest.d.ts +5 -3
- package/_types/utils/buildRequest.d.ts.map +1 -1
- package/_types/utils/ccip.d.ts +5 -3
- package/_types/utils/ccip.d.ts.map +1 -1
- package/_types/utils/promise/withRetry.d.ts +3 -2
- package/_types/utils/promise/withRetry.d.ts.map +1 -1
- package/_types/utils/promise/withTimeout.d.ts +1 -1
- package/_types/utils/promise/withTimeout.d.ts.map +1 -1
- package/_types/utils/rpc/http.d.ts +1 -1
- package/_types/utils/rpc/http.d.ts.map +1 -1
- package/_types/utils/wait.d.ts +3 -1
- package/_types/utils/wait.d.ts.map +1 -1
- package/actions/public/call.ts +59 -23
- package/clients/transports/http.ts +18 -2
- package/errors/utils.ts +19 -0
- package/errors/version.ts +1 -1
- package/index.ts +1 -0
- package/package.json +1 -1
- package/tempo/Formatters.ts +20 -4
- package/tempo/actions/wallet.ts +85 -21
- package/types/eip1193.ts +2 -0
- package/utils/buildRequest.ts +22 -6
- package/utils/ccip.ts +22 -4
- package/utils/promise/withRetry.ts +29 -2
- package/utils/promise/withTimeout.ts +6 -3
- package/utils/rpc/http.ts +7 -1
- package/utils/wait.ts +24 -2
|
@@ -81,6 +81,17 @@ export type HttpTransportErrorType =
|
|
|
81
81
|
| UrlRequiredErrorType
|
|
82
82
|
| ErrorType
|
|
83
83
|
|
|
84
|
+
let signalId = 0
|
|
85
|
+
const signalIds = new WeakMap<AbortSignal, number>()
|
|
86
|
+
function getSignalId(signal: AbortSignal | undefined) {
|
|
87
|
+
if (!signal) return 'default'
|
|
88
|
+
const id = signalIds.get(signal)
|
|
89
|
+
if (id !== undefined) return id
|
|
90
|
+
const nextId = signalId++
|
|
91
|
+
signalIds.set(signal, nextId)
|
|
92
|
+
return nextId
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
/**
|
|
85
96
|
* @description Creates a HTTP transport that connects to a JSON-RPC API.
|
|
86
97
|
*/
|
|
@@ -125,11 +136,14 @@ export function http<
|
|
|
125
136
|
key,
|
|
126
137
|
methods,
|
|
127
138
|
name,
|
|
128
|
-
async request({ method, params }) {
|
|
139
|
+
async request({ method, params }, options) {
|
|
129
140
|
const body = { method, params }
|
|
141
|
+
const fetchOptions = options?.signal
|
|
142
|
+
? { signal: options.signal }
|
|
143
|
+
: undefined
|
|
130
144
|
|
|
131
145
|
const { schedule } = createBatchScheduler({
|
|
132
|
-
id: url_
|
|
146
|
+
id: `${url_}.${getSignalId(options?.signal)}`,
|
|
133
147
|
wait,
|
|
134
148
|
shouldSplitBatch(requests) {
|
|
135
149
|
return requests.length > batchSize
|
|
@@ -137,6 +151,7 @@ export function http<
|
|
|
137
151
|
fn: (body: RpcRequest[]) =>
|
|
138
152
|
rpcClient.request({
|
|
139
153
|
body,
|
|
154
|
+
fetchOptions,
|
|
140
155
|
}),
|
|
141
156
|
sort: (a, b) => a.id - b.id,
|
|
142
157
|
})
|
|
@@ -147,6 +162,7 @@ export function http<
|
|
|
147
162
|
: [
|
|
148
163
|
await rpcClient.request({
|
|
149
164
|
body,
|
|
165
|
+
fetchOptions,
|
|
150
166
|
}),
|
|
151
167
|
]
|
|
152
168
|
|
package/errors/utils.ts
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
import type { Address } from 'abitype'
|
|
2
2
|
|
|
3
3
|
export type ErrorType<name extends string = 'Error'> = Error & { name: name }
|
|
4
|
+
export type AbortErrorType = ErrorType<'AbortError'>
|
|
4
5
|
|
|
5
6
|
export const getContractAddress = (address: Address) => address
|
|
6
7
|
|
|
8
|
+
export function getAbortError(signal?: AbortSignal | undefined) {
|
|
9
|
+
if (signal?.reason) return signal.reason
|
|
10
|
+
if (typeof DOMException === 'function')
|
|
11
|
+
return new DOMException('This operation was aborted', 'AbortError')
|
|
12
|
+
const error = new Error('This operation was aborted') as AbortErrorType
|
|
13
|
+
error.name = 'AbortError'
|
|
14
|
+
return error
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isAbortError(error: unknown): error is AbortErrorType {
|
|
18
|
+
return (
|
|
19
|
+
typeof error === 'object' &&
|
|
20
|
+
error !== null &&
|
|
21
|
+
'name' in error &&
|
|
22
|
+
error.name === 'AbortError'
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
7
26
|
/**
|
|
8
27
|
* Returns the URL with any embedded basic-auth credentials stripped, so
|
|
9
28
|
* error messages and logs don't leak secrets when an RPC URL like
|
package/errors/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.
|
|
1
|
+
export const version = '2.49.2'
|
package/index.ts
CHANGED
package/package.json
CHANGED
package/tempo/Formatters.ts
CHANGED
|
@@ -122,12 +122,15 @@ export function formatTransactionRequest(
|
|
|
122
122
|
const [keyType, keyData] = (() => {
|
|
123
123
|
const type =
|
|
124
124
|
account && 'keyType' in account ? account.keyType : account?.source
|
|
125
|
-
if (!type) return [request.keyType, request.keyData]
|
|
125
|
+
if (!type) return [request.keyType, shimKeyData(request.keyData)]
|
|
126
126
|
if (type === 'webAuthn')
|
|
127
|
-
//
|
|
128
|
-
|
|
127
|
+
// Send a 2-byte big-endian length hint (1400 = 0x0578) instead of a
|
|
128
|
+
// 1400-byte dummy blob. The node's gas estimator expects key_data to
|
|
129
|
+
// be 1, 2, or 4 bytes encoding the desired WebAuthn signature size;
|
|
130
|
+
// anything else falls back to the 800-byte default.
|
|
131
|
+
return ['webAuthn', '0x0578']
|
|
129
132
|
if (['p256', 'secp256k1'].includes(type)) return [type, undefined]
|
|
130
|
-
return [request.keyType, request.keyData]
|
|
133
|
+
return [request.keyType, shimKeyData(request.keyData)]
|
|
131
134
|
})()
|
|
132
135
|
|
|
133
136
|
const keyId =
|
|
@@ -157,3 +160,16 @@ export function formatTransactionRequest(
|
|
|
157
160
|
: {}),
|
|
158
161
|
} as never
|
|
159
162
|
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Auto-shim user-provided keyData that is longer than 4 bytes into a
|
|
166
|
+
* 2-byte big-endian length hint. The node gas estimator only accepts
|
|
167
|
+
* 1, 2, or 4-byte key_data as a size hint; anything else silently falls
|
|
168
|
+
* back to the 800-byte default.
|
|
169
|
+
*/
|
|
170
|
+
function shimKeyData(data: Hex.Hex | undefined): Hex.Hex | undefined {
|
|
171
|
+
if (!data) return data
|
|
172
|
+
const byteLength = (data.length - 2) / 2 // subtract "0x" prefix
|
|
173
|
+
if (byteLength <= 4) return data
|
|
174
|
+
return Hex.fromNumber(byteLength, { size: 2 })
|
|
175
|
+
}
|
package/tempo/actions/wallet.ts
CHANGED
|
@@ -3,11 +3,18 @@ import type { Client } from '../../clients/createClient.js'
|
|
|
3
3
|
import type { Transport } from '../../clients/transports/createTransport.js'
|
|
4
4
|
import type { ErrorType as CoreErrorType } from '../../errors/utils.js'
|
|
5
5
|
import type { Chain } from '../../types/chain.js'
|
|
6
|
+
import type { OneOf } from '../../types/utils.js'
|
|
6
7
|
import type { RequestErrorType } from '../../utils/buildRequest.js'
|
|
7
8
|
import type { TransactionReceipt } from '../Transaction.js'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Transfers a TIP-20 token. Discriminated on `editable`:
|
|
12
|
+
*
|
|
13
|
+
* - omitted or `false` (default): read-only. Uses an access key when
|
|
14
|
+
* one matches (signs without showing the wallet UI), otherwise falls
|
|
15
|
+
* back to a confirm dialog the user has to approve.
|
|
16
|
+
* - `true`: editable. Opens the wallet send UI with the supplied fields
|
|
17
|
+
* pre-filled for the user to confirm or edit before signing.
|
|
11
18
|
*
|
|
12
19
|
* @example
|
|
13
20
|
* ```ts
|
|
@@ -18,53 +25,110 @@ import type { TransactionReceipt } from '../Transaction.js'
|
|
|
18
25
|
* transport: custom(window.ethereum),
|
|
19
26
|
* })
|
|
20
27
|
*
|
|
21
|
-
*
|
|
28
|
+
* // Read-only (no UI when an access key matches)
|
|
29
|
+
* const { receipt } = await Actions.wallet.transfer(client, {
|
|
30
|
+
* amount: '1.5',
|
|
22
31
|
* to: '0x...',
|
|
23
32
|
* token: '0x...',
|
|
24
|
-
*
|
|
33
|
+
* })
|
|
34
|
+
*
|
|
35
|
+
* // Editable (opens wallet UI)
|
|
36
|
+
* await Actions.wallet.transfer(client, {
|
|
37
|
+
* editable: true,
|
|
38
|
+
* token: 'pathUSD',
|
|
25
39
|
* })
|
|
26
40
|
* ```
|
|
27
41
|
*
|
|
28
42
|
* @param client - Client.
|
|
29
43
|
* @param parameters - Parameters.
|
|
30
|
-
* @returns The submitted
|
|
44
|
+
* @returns The submitted transfer receipt and chain ID.
|
|
31
45
|
*/
|
|
32
|
-
export async function
|
|
46
|
+
export async function transfer<chain extends Chain | undefined>(
|
|
33
47
|
client: Client<Transport, chain>,
|
|
34
|
-
parameters:
|
|
35
|
-
): Promise<
|
|
48
|
+
parameters: transfer.Parameters,
|
|
49
|
+
): Promise<transfer.ReturnValue> {
|
|
36
50
|
return client.request<{
|
|
37
|
-
Method: '
|
|
38
|
-
Parameters: [
|
|
39
|
-
ReturnType:
|
|
51
|
+
Method: 'wallet_transfer'
|
|
52
|
+
Parameters: [transfer.Parameters]
|
|
53
|
+
ReturnType: transfer.ReturnValue
|
|
40
54
|
}>(
|
|
41
55
|
{
|
|
42
|
-
method: '
|
|
56
|
+
method: 'wallet_transfer',
|
|
43
57
|
params: [parameters],
|
|
44
58
|
},
|
|
45
59
|
{ retryCount: 0 },
|
|
46
60
|
)
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
export declare namespace
|
|
63
|
+
export declare namespace transfer {
|
|
64
|
+
/**
|
|
65
|
+
* Read-only variant — uses an access key when one matches, otherwise
|
|
66
|
+
* shows a confirm dialog.
|
|
67
|
+
*/
|
|
68
|
+
type ReadOnly = {
|
|
69
|
+
/** Human-readable amount to transfer (for example, `"1.5"`). */
|
|
70
|
+
amount: string
|
|
71
|
+
/**
|
|
72
|
+
* Skip the editable wallet UI. The wallet still shows a confirm
|
|
73
|
+
* dialog when no matching access key is available.
|
|
74
|
+
* @default false
|
|
75
|
+
*/
|
|
76
|
+
editable?: false | undefined
|
|
77
|
+
/**
|
|
78
|
+
* Address to transfer tokens from. Defaults to the active account. When
|
|
79
|
+
* set to a different address, the call uses `transferFrom` and requires
|
|
80
|
+
* the active account to have an allowance from `from`.
|
|
81
|
+
*/
|
|
82
|
+
from?: Address | undefined
|
|
83
|
+
/**
|
|
84
|
+
* UTF-8 memo (max 32 bytes) to attach to the transfer.
|
|
85
|
+
*/
|
|
86
|
+
memo?: string | undefined
|
|
87
|
+
/** Recipient address. */
|
|
88
|
+
to: Address
|
|
89
|
+
/**
|
|
90
|
+
* Token to transfer, accepted as either a contract address or a curated
|
|
91
|
+
* tokenlist symbol (case-insensitive, for example `"pathUsd"`). Symbols
|
|
92
|
+
* are resolved against the curated tokenlist on the active chain.
|
|
93
|
+
*/
|
|
94
|
+
token: Address | string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Editable variant — opens the wallet send UI with optional pre-filled fields. */
|
|
98
|
+
type Editable = {
|
|
99
|
+
/** Human-readable amount to pre-fill (for example, `"1.5"`). */
|
|
100
|
+
amount?: string | undefined
|
|
101
|
+
/** Show the wallet UI for the user to confirm or edit. */
|
|
102
|
+
editable: true
|
|
103
|
+
/**
|
|
104
|
+
* UTF-8 memo (max 32 bytes) to attach to the transfer.
|
|
105
|
+
*/
|
|
106
|
+
memo?: string | undefined
|
|
107
|
+
/** Recipient address to pre-fill. */
|
|
108
|
+
to?: Address | undefined
|
|
109
|
+
/**
|
|
110
|
+
* Token to pre-fill, accepted as either a contract address or a curated
|
|
111
|
+
* tokenlist symbol (case-insensitive, for example `"pathUsd"`). Symbols
|
|
112
|
+
* are resolved against the curated tokenlist on the active chain. Omit
|
|
113
|
+
* to let the user choose.
|
|
114
|
+
*/
|
|
115
|
+
token?: Address | string | undefined
|
|
116
|
+
}
|
|
117
|
+
|
|
50
118
|
export type Parameters = {
|
|
119
|
+
/** Chain id. Defaults to the active chain. */
|
|
120
|
+
chainId?: number | undefined
|
|
51
121
|
/**
|
|
52
122
|
* Fee payer override. `false` to disable the wallet's default fee payer,
|
|
53
123
|
* a URL string to use a custom fee payer service.
|
|
54
124
|
*/
|
|
55
125
|
feePayer?: boolean | string | undefined
|
|
56
|
-
|
|
57
|
-
to?: Address | undefined
|
|
58
|
-
/** Token contract address to pre-fill. Omit to let the user choose. */
|
|
59
|
-
token?: Address | undefined
|
|
60
|
-
/** Human-readable amount to pre-fill (for example, "1.5"). */
|
|
61
|
-
value?: string | undefined
|
|
62
|
-
}
|
|
126
|
+
} & OneOf<ReadOnly | Editable>
|
|
63
127
|
|
|
64
128
|
export type ReturnValue = {
|
|
65
|
-
/** Chain ID the
|
|
129
|
+
/** Chain ID the transfer was submitted to. */
|
|
66
130
|
chainId: number
|
|
67
|
-
/** Receipt of the submitted
|
|
131
|
+
/** Receipt of the submitted transfer. */
|
|
68
132
|
receipt: TransactionReceipt
|
|
69
133
|
}
|
|
70
134
|
|
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
|
}
|
package/utils/buildRequest.ts
CHANGED
|
@@ -66,7 +66,12 @@ import {
|
|
|
66
66
|
WalletConnectSessionSettlementError,
|
|
67
67
|
type WalletConnectSessionSettlementErrorType,
|
|
68
68
|
} from '../errors/rpc.js'
|
|
69
|
-
import
|
|
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<
|
|
119
|
-
request
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
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:
|
|
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
|
|
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)
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
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
|
}
|