@chainlink/ccip-sdk 0.90.2 → 0.91.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.
- package/README.md +35 -26
- package/dist/aptos/exec.d.ts +4 -5
- package/dist/aptos/exec.d.ts.map +1 -1
- package/dist/aptos/exec.js +5 -14
- package/dist/aptos/exec.js.map +1 -1
- package/dist/aptos/hasher.d.ts +18 -0
- package/dist/aptos/hasher.d.ts.map +1 -1
- package/dist/aptos/hasher.js +18 -0
- package/dist/aptos/hasher.js.map +1 -1
- package/dist/aptos/index.d.ts +127 -28
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +199 -70
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.d.ts +18 -0
- package/dist/aptos/logs.d.ts.map +1 -1
- package/dist/aptos/logs.js +21 -3
- package/dist/aptos/logs.js.map +1 -1
- package/dist/aptos/send.d.ts +22 -5
- package/dist/aptos/send.d.ts.map +1 -1
- package/dist/aptos/send.js +23 -15
- package/dist/aptos/send.js.map +1 -1
- package/dist/aptos/token.d.ts +6 -0
- package/dist/aptos/token.d.ts.map +1 -1
- package/dist/aptos/token.js +6 -0
- package/dist/aptos/token.js.map +1 -1
- package/dist/aptos/types.d.ts +16 -1
- package/dist/aptos/types.d.ts.map +1 -1
- package/dist/aptos/types.js +13 -0
- package/dist/aptos/types.js.map +1 -1
- package/dist/aptos/utils.d.ts +1 -1
- package/dist/aptos/utils.js +1 -1
- package/dist/chain.d.ts +185 -99
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +38 -15
- package/dist/chain.js.map +1 -1
- package/dist/commits.d.ts +4 -10
- package/dist/commits.d.ts.map +1 -1
- package/dist/commits.js +2 -1
- package/dist/commits.js.map +1 -1
- package/dist/evm/const.d.ts +5 -0
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +5 -0
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/errors.d.ts +5 -0
- package/dist/evm/errors.d.ts.map +1 -1
- package/dist/evm/errors.js +6 -1
- package/dist/evm/errors.js.map +1 -1
- package/dist/evm/hasher.d.ts +16 -2
- package/dist/evm/hasher.d.ts.map +1 -1
- package/dist/evm/hasher.js +17 -3
- package/dist/evm/hasher.js.map +1 -1
- package/dist/evm/index.d.ts +176 -31
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +312 -154
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/logs.d.ts +20 -0
- package/dist/evm/logs.d.ts.map +1 -0
- package/dist/evm/logs.js +194 -0
- package/dist/evm/logs.js.map +1 -0
- package/dist/evm/messages.d.ts +11 -2
- package/dist/evm/messages.d.ts.map +1 -1
- package/dist/evm/messages.js +4 -2
- package/dist/evm/messages.js.map +1 -1
- package/dist/evm/offchain.d.ts +7 -2
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +12 -7
- package/dist/evm/offchain.js.map +1 -1
- package/dist/execution.d.ts +19 -62
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +28 -31
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +35 -5
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +10 -5
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +6 -8
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +7 -9
- package/dist/gas.js.map +1 -1
- package/dist/hasher/common.d.ts +3 -2
- package/dist/hasher/common.d.ts.map +1 -1
- package/dist/hasher/common.js +2 -2
- package/dist/hasher/common.js.map +1 -1
- package/dist/hasher/hasher.d.ts +8 -2
- package/dist/hasher/hasher.d.ts.map +1 -1
- package/dist/hasher/hasher.js +8 -3
- package/dist/hasher/hasher.js.map +1 -1
- package/dist/hasher/merklemulti.d.ts +11 -9
- package/dist/hasher/merklemulti.d.ts.map +1 -1
- package/dist/hasher/merklemulti.js +17 -16
- package/dist/hasher/merklemulti.js.map +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +39 -25
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +42 -35
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts +1 -1
- package/dist/solana/cleanup.d.ts +14 -10
- package/dist/solana/cleanup.d.ts.map +1 -1
- package/dist/solana/cleanup.js +35 -33
- package/dist/solana/cleanup.js.map +1 -1
- package/dist/solana/exec.d.ts +19 -11
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +86 -163
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/hasher.d.ts +7 -2
- package/dist/solana/hasher.d.ts.map +1 -1
- package/dist/solana/hasher.js +7 -2
- package/dist/solana/hasher.js.map +1 -1
- package/dist/solana/index.d.ts +202 -84
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +367 -252
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.d.ts +8 -18
- package/dist/solana/offchain.d.ts.map +1 -1
- package/dist/solana/offchain.js +29 -83
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/patchBorsh.d.ts +5 -1
- package/dist/solana/patchBorsh.d.ts.map +1 -1
- package/dist/solana/patchBorsh.js +57 -46
- package/dist/solana/patchBorsh.js.map +1 -1
- package/dist/solana/send.d.ts +28 -10
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +44 -77
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/types.d.ts +22 -1
- package/dist/solana/types.d.ts.map +1 -1
- package/dist/solana/types.js +12 -1
- package/dist/solana/types.js.map +1 -1
- package/dist/solana/utils.d.ts +58 -4
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +110 -7
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/hasher.d.ts +18 -0
- package/dist/sui/hasher.d.ts.map +1 -1
- package/dist/sui/hasher.js +18 -0
- package/dist/sui/hasher.js.map +1 -1
- package/dist/sui/index.d.ts +99 -12
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +108 -19
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/types.d.ts +6 -0
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js +5 -0
- package/dist/sui/types.js.map +1 -1
- package/dist/supported-chains.d.ts +2 -1
- package/dist/supported-chains.d.ts.map +1 -1
- package/dist/supported-chains.js.map +1 -1
- package/dist/types.d.ts +127 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +67 -46
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +143 -21
- package/dist/utils.js.map +1 -1
- package/package.json +13 -9
- package/src/aptos/exec.ts +7 -18
- package/src/aptos/hasher.ts +18 -0
- package/src/aptos/index.ts +288 -110
- package/src/aptos/logs.ts +21 -3
- package/src/aptos/send.ts +25 -22
- package/src/aptos/token.ts +6 -0
- package/src/aptos/types.ts +26 -2
- package/src/aptos/utils.ts +1 -1
- package/src/chain.ts +243 -108
- package/src/commits.ts +6 -7
- package/src/evm/const.ts +5 -0
- package/src/evm/errors.ts +6 -1
- package/src/evm/hasher.ts +20 -4
- package/src/evm/index.ts +416 -214
- package/src/evm/logs.ts +255 -0
- package/src/evm/messages.ts +11 -5
- package/src/evm/offchain.ts +13 -4
- package/src/execution.ts +40 -32
- package/src/extra-args.ts +38 -6
- package/src/gas.ts +7 -9
- package/src/hasher/common.ts +3 -2
- package/src/hasher/hasher.ts +12 -4
- package/src/hasher/merklemulti.ts +17 -16
- package/src/index.ts +29 -23
- package/src/requests.ts +64 -46
- package/src/selectors.ts +1 -1
- package/src/solana/cleanup.ts +49 -34
- package/src/solana/exec.ts +128 -272
- package/src/solana/hasher.ts +13 -4
- package/src/solana/index.ts +483 -356
- package/src/solana/offchain.ts +32 -102
- package/src/solana/patchBorsh.ts +65 -50
- package/src/solana/send.ts +52 -111
- package/src/solana/types.ts +44 -3
- package/src/solana/utils.ts +143 -19
- package/src/sui/hasher.ts +18 -0
- package/src/sui/index.ts +143 -31
- package/src/sui/types.ts +6 -0
- package/src/supported-chains.ts +2 -1
- package/src/types.ts +130 -18
- package/src/utils.ts +168 -26
- package/tsconfig.json +2 -1
package/src/types.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype'
|
|
2
2
|
import type { BytesLike, Log } from 'ethers'
|
|
3
3
|
|
|
4
|
-
import type { ChainFamily, ChainTransaction } from './chain.ts'
|
|
5
4
|
import type OffRamp_1_6_ABI from './evm/abi/OffRamp_1_6.ts'
|
|
6
5
|
import type { CCIPMessage_EVM, CCIPMessage_V1_6_EVM } from './evm/messages.ts'
|
|
7
6
|
import type { ExtraArgs } from './extra-args.ts'
|
|
@@ -11,24 +10,28 @@ import type { CCIPMessage_V1_6_Sui } from './sui/types.ts'
|
|
|
11
10
|
export type { CCIPMessage_V1_6 } from './evm/messages.ts'
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* Logger interface for logging messages (compatible with console)
|
|
15
14
|
*/
|
|
16
|
-
export type
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
export type Logger = {
|
|
16
|
+
debug: (...args: unknown[]) => void
|
|
17
|
+
info: (...args: unknown[]) => void
|
|
18
|
+
warn: (...args: unknown[]) => void
|
|
19
|
+
error: (...args: unknown[]) => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* An options object which may have a logger
|
|
24
|
+
*/
|
|
25
|
+
export type WithLogger = {
|
|
26
|
+
logger?: Logger
|
|
27
|
+
}
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
|
-
* "Fix" for deeply intersecting types containing arrays: A[] & B[]
|
|
28
|
-
* Usually, if you intersect { arr: A[] } & { arr: B[] }
|
|
30
|
+
* "Fix" for deeply intersecting types containing arrays: converts `A[] & B[]` to `(A & B)[]`.
|
|
31
|
+
* Usually, if you intersect `\{ arr: A[] \} & \{ arr: B[] \}`, arr will have type `A[] & B[]`,
|
|
29
32
|
* i.e. all/each *index* of A[] and B[] should be present in the intersection, with quite undefined
|
|
30
|
-
* types of the elements themselves, oftentimes assigning only one of A or B to the element type
|
|
31
|
-
* This converts deeply to (A & B)[]
|
|
33
|
+
* types of the elements themselves, oftentimes assigning only one of A or B to the element type.
|
|
34
|
+
* This converts deeply to `(A & B)[]`, i.e. each *element* should have all properties of A & B.
|
|
32
35
|
*/
|
|
33
36
|
export type MergeArrayElements<T, U> = {
|
|
34
37
|
[K in keyof (T & U)]: K extends keyof T & keyof U
|
|
@@ -52,13 +55,30 @@ export type MergeArrayElements<T, U> = {
|
|
|
52
55
|
: never
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Enumeration of supported blockchain families.
|
|
60
|
+
*/
|
|
61
|
+
export const ChainFamily = {
|
|
62
|
+
EVM: 'evm',
|
|
63
|
+
Solana: 'solana',
|
|
64
|
+
Aptos: 'aptos',
|
|
65
|
+
Sui: 'sui',
|
|
66
|
+
} as const
|
|
67
|
+
/** Type representing one of the supported chain families. */
|
|
68
|
+
export type ChainFamily = (typeof ChainFamily)[keyof typeof ChainFamily]
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Enumeration of supported CCIP protocol versions.
|
|
72
|
+
*/
|
|
55
73
|
export const CCIPVersion = {
|
|
56
74
|
V1_2: '1.2.0',
|
|
57
75
|
V1_5: '1.5.0',
|
|
58
76
|
V1_6: '1.6.0',
|
|
59
77
|
} as const
|
|
78
|
+
/** Type representing one of the supported CCIP versions. */
|
|
60
79
|
export type CCIPVersion = (typeof CCIPVersion)[keyof typeof CCIPVersion]
|
|
61
80
|
|
|
81
|
+
/** Helper type that maps chain family to its chain ID format. */
|
|
62
82
|
type ChainFamilyWithId<F extends ChainFamily> = F extends typeof ChainFamily.EVM
|
|
63
83
|
? { readonly family: F; readonly chainId: number }
|
|
64
84
|
: F extends typeof ChainFamily.Solana
|
|
@@ -67,87 +87,179 @@ type ChainFamilyWithId<F extends ChainFamily> = F extends typeof ChainFamily.EVM
|
|
|
67
87
|
? { readonly family: F; readonly chainId: `${F}:${number}` }
|
|
68
88
|
: never
|
|
69
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Network information including chain selector and metadata.
|
|
92
|
+
*/
|
|
70
93
|
export type NetworkInfo<F extends ChainFamily = ChainFamily> = {
|
|
94
|
+
/** Unique chain selector used by CCIP. */
|
|
71
95
|
readonly chainSelector: bigint
|
|
96
|
+
/** Human-readable network name. */
|
|
72
97
|
readonly name: string
|
|
98
|
+
/** Whether this is a testnet. */
|
|
73
99
|
readonly isTestnet: boolean
|
|
74
100
|
} & ChainFamilyWithId<F>
|
|
75
101
|
|
|
102
|
+
/**
|
|
103
|
+
* CCIP lane configuration connecting source and destination chains.
|
|
104
|
+
*/
|
|
76
105
|
export interface Lane<V extends CCIPVersion = CCIPVersion> {
|
|
106
|
+
/** Source chain selector. */
|
|
77
107
|
sourceChainSelector: bigint
|
|
108
|
+
/** Destination chain selector. */
|
|
78
109
|
destChainSelector: bigint
|
|
110
|
+
/** OnRamp contract address on source chain. */
|
|
79
111
|
onRamp: string
|
|
112
|
+
/** CCIP protocol version for this lane. */
|
|
80
113
|
version: V
|
|
81
114
|
}
|
|
82
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Union type representing a CCIP message across different versions and chain families.
|
|
118
|
+
*/
|
|
83
119
|
export type CCIPMessage<V extends CCIPVersion = CCIPVersion> = V extends
|
|
84
120
|
| typeof CCIPVersion.V1_2
|
|
85
121
|
| typeof CCIPVersion.V1_5
|
|
86
122
|
? CCIPMessage_EVM<V>
|
|
87
123
|
: CCIPMessage_V1_6_EVM | CCIPMessage_V1_6_Solana | CCIPMessage_V1_6_Sui
|
|
88
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Generic log structure compatible across chain families.
|
|
127
|
+
*/
|
|
89
128
|
export type Log_ = Pick<Log, 'topics' | 'index' | 'address' | 'blockNumber' | 'transactionHash'> & {
|
|
129
|
+
/** Log data as bytes or parsed object. */
|
|
90
130
|
data: BytesLike | Record<string, unknown>
|
|
131
|
+
/** Optional reference to the containing transaction. */
|
|
91
132
|
tx?: ChainTransaction
|
|
92
133
|
}
|
|
93
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Generic transaction structure compatible across chain families.
|
|
137
|
+
*/
|
|
138
|
+
export type ChainTransaction = {
|
|
139
|
+
/** Transaction hash. */
|
|
140
|
+
hash: string
|
|
141
|
+
/** Logs emitted by this transaction. */
|
|
142
|
+
logs: readonly Log_[]
|
|
143
|
+
/** Block number containing this transaction. */
|
|
144
|
+
blockNumber: number
|
|
145
|
+
/** Unix timestamp of the block. */
|
|
146
|
+
timestamp: number
|
|
147
|
+
/** Sender address. */
|
|
148
|
+
from: string
|
|
149
|
+
/** Optional error if transaction failed. */
|
|
150
|
+
error?: unknown
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Complete CCIP request containing lane, message, log, and transaction info.
|
|
155
|
+
*/
|
|
94
156
|
export interface CCIPRequest<V extends CCIPVersion = CCIPVersion> {
|
|
157
|
+
/** Lane configuration for this request. */
|
|
95
158
|
lane: Lane<V>
|
|
159
|
+
/** The CCIP message being sent. */
|
|
96
160
|
message: CCIPMessage<V>
|
|
161
|
+
/** Log event from the OnRamp. */
|
|
97
162
|
log: Log_
|
|
98
|
-
|
|
99
|
-
|
|
163
|
+
/** Transaction that emitted the request. */
|
|
164
|
+
tx: Pick<ChainTransaction, 'hash' | 'logs' | 'blockNumber' | 'timestamp' | 'from' | 'error'>
|
|
100
165
|
}
|
|
101
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Commit report structure from the OffRamp CommitReportAccepted event.
|
|
169
|
+
*/
|
|
102
170
|
export type CommitReport = AbiParametersToPrimitiveTypes<
|
|
103
171
|
ExtractAbiEvent<typeof OffRamp_1_6_ABI, 'CommitReportAccepted'>['inputs']
|
|
104
172
|
>[0][number]
|
|
105
173
|
|
|
174
|
+
/**
|
|
175
|
+
* CCIP commit information containing the report and its log.
|
|
176
|
+
*/
|
|
106
177
|
export interface CCIPCommit {
|
|
178
|
+
/** The commit report data. */
|
|
107
179
|
report: CommitReport
|
|
180
|
+
/** Log event from the commit. */
|
|
108
181
|
log: Log_
|
|
109
182
|
}
|
|
110
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Enumeration of possible execution states for a CCIP message.
|
|
186
|
+
*/
|
|
111
187
|
export const ExecutionState = {
|
|
188
|
+
/** Execution is in progress. */
|
|
112
189
|
InProgress: 1,
|
|
190
|
+
/** Execution completed successfully. */
|
|
113
191
|
Success: 2,
|
|
192
|
+
/** Execution failed. */
|
|
114
193
|
Failed: 3,
|
|
115
194
|
} as const
|
|
195
|
+
/** Type representing an execution state value. */
|
|
116
196
|
export type ExecutionState = (typeof ExecutionState)[keyof typeof ExecutionState]
|
|
117
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Receipt of a CCIP message execution on the destination chain.
|
|
200
|
+
*/
|
|
118
201
|
export type ExecutionReceipt = {
|
|
202
|
+
/** Unique message identifier. */
|
|
119
203
|
messageId: string
|
|
204
|
+
/** Sequence number of the message. */
|
|
120
205
|
sequenceNumber: bigint
|
|
206
|
+
/** Current execution state. */
|
|
121
207
|
state: ExecutionState
|
|
208
|
+
/** Source chain selector (if available). */
|
|
122
209
|
sourceChainSelector?: bigint
|
|
210
|
+
/** Hash of the message (if available). */
|
|
123
211
|
messageHash?: string
|
|
212
|
+
/** Return data from the receiver contract (if any). */
|
|
124
213
|
returnData?: BytesLike | Record<string, string>
|
|
214
|
+
/** Gas consumed by execution (if available). */
|
|
125
215
|
gasUsed?: bigint
|
|
126
216
|
}
|
|
127
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Complete CCIP execution event with receipt, log, and timestamp.
|
|
220
|
+
*/
|
|
128
221
|
export interface CCIPExecution {
|
|
222
|
+
/** Execution receipt data. */
|
|
129
223
|
receipt: ExecutionReceipt
|
|
224
|
+
/** Log event from the execution. */
|
|
130
225
|
log: Log_
|
|
226
|
+
/** Unix timestamp of the execution. */
|
|
131
227
|
timestamp: number
|
|
132
228
|
}
|
|
133
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Offchain token data for CCTP or other bridge attestations.
|
|
232
|
+
*/
|
|
134
233
|
export type OffchainTokenData = { _tag: string; [k: string]: BytesLike } | undefined
|
|
135
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Execution report containing message, proofs, and offchain token data.
|
|
237
|
+
*/
|
|
136
238
|
export type ExecutionReport<M extends CCIPMessage = CCIPMessage> = {
|
|
239
|
+
/** The CCIP message to execute. */
|
|
137
240
|
message: M
|
|
241
|
+
/** Merkle proofs for the message. */
|
|
138
242
|
proofs: readonly BytesLike[]
|
|
243
|
+
/** Bit flags for proof verification. */
|
|
139
244
|
proofFlagBits: bigint
|
|
245
|
+
/** Merkle root for verification. */
|
|
140
246
|
merkleRoot: string
|
|
247
|
+
/** Offchain token data for each token transfer. */
|
|
141
248
|
offchainTokenData: readonly OffchainTokenData[]
|
|
142
249
|
}
|
|
143
250
|
|
|
144
251
|
/**
|
|
145
|
-
* A message to be sent to another network
|
|
252
|
+
* A message to be sent to another network.
|
|
146
253
|
*/
|
|
147
254
|
export type AnyMessage = {
|
|
255
|
+
/** Receiver address on the destination chain. */
|
|
148
256
|
receiver: BytesLike
|
|
257
|
+
/** Arbitrary data payload. */
|
|
149
258
|
data: BytesLike
|
|
259
|
+
/** Extra arguments for gas limits and other settings. */
|
|
150
260
|
extraArgs: ExtraArgs
|
|
261
|
+
/** Optional token transfers. */
|
|
151
262
|
tokenAmounts?: readonly { token: string; amount: bigint }[]
|
|
263
|
+
/** Optional fee token address (native if omitted). */
|
|
152
264
|
feeToken?: string
|
|
153
265
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Buffer } from 'buffer'
|
|
2
2
|
|
|
3
3
|
import bs58 from 'bs58'
|
|
4
4
|
import {
|
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
decodeBase64,
|
|
9
9
|
getBytes,
|
|
10
10
|
isBytesLike,
|
|
11
|
-
|
|
11
|
+
toBeArray,
|
|
12
12
|
toBigInt,
|
|
13
13
|
} from 'ethers'
|
|
14
|
-
import
|
|
14
|
+
import { memoize } from 'micro-memoize'
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import type { Chain } from './chain.ts'
|
|
17
17
|
import SELECTORS from './selectors.ts'
|
|
18
18
|
import { supportedChains } from './supported-chains.ts'
|
|
19
|
-
import type
|
|
19
|
+
import { type NetworkInfo, type WithLogger, ChainFamily } from './types.ts'
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Returns *some* block number with timestamp prior to `timestamp`
|
|
@@ -31,7 +31,7 @@ export async function getSomeBlockNumberBefore(
|
|
|
31
31
|
getBlockTimestamp: (blockNumber: number) => Promise<number>,
|
|
32
32
|
recentBlockNumber: number,
|
|
33
33
|
timestamp: number,
|
|
34
|
-
precision = 10,
|
|
34
|
+
{ precision = 10, logger = console }: { precision?: number } & WithLogger = {},
|
|
35
35
|
): Promise<number> {
|
|
36
36
|
let beforeBlockNumber = Math.max(1, recentBlockNumber - precision * 1000)
|
|
37
37
|
let beforeTimestamp = await getBlockTimestamp(beforeBlockNumber)
|
|
@@ -78,7 +78,7 @@ export async function getSomeBlockNumberBefore(
|
|
|
78
78
|
beforeBlockNumber = pivot
|
|
79
79
|
beforeTimestamp = pivotTimestamp
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
logger.debug('getSomeBlockNumberBefore: searching block before', {
|
|
82
82
|
beforeBlockNumber,
|
|
83
83
|
beforeTimestamp,
|
|
84
84
|
pivot,
|
|
@@ -94,7 +94,7 @@ export async function getSomeBlockNumberBefore(
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// memoized so we always output the same object for a given chainId
|
|
97
|
-
const networkInfoFromChainId =
|
|
97
|
+
const networkInfoFromChainId = memoize((chainId: NetworkInfo['chainId']): NetworkInfo => {
|
|
98
98
|
const sel = SELECTORS[chainId]
|
|
99
99
|
if (!sel?.name) throw new Error(`Chain not found: ${chainId}`)
|
|
100
100
|
return {
|
|
@@ -115,7 +115,7 @@ const networkInfoFromChainId = moize.default((chainId: NetworkInfo['chainId']):
|
|
|
115
115
|
* - Chain name as string ("ethereum-mainnet")
|
|
116
116
|
* @returns Complete NetworkInfo object
|
|
117
117
|
*/
|
|
118
|
-
export const networkInfo =
|
|
118
|
+
export const networkInfo = memoize(function networkInfo_(
|
|
119
119
|
selectorOrIdOrName: bigint | number | string,
|
|
120
120
|
): NetworkInfo {
|
|
121
121
|
let chainId
|
|
@@ -181,6 +181,12 @@ export function* blockRangeGenerator(
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* JSON replacer function that converts BigInt values to strings.
|
|
186
|
+
* @param _key - Property key (unused).
|
|
187
|
+
* @param value - Value to transform.
|
|
188
|
+
* @returns String representation if BigInt, otherwise unchanged value.
|
|
189
|
+
*/
|
|
184
190
|
export function bigIntReplacer(_key: string, value: unknown): unknown {
|
|
185
191
|
if (typeof value === 'bigint') {
|
|
186
192
|
return value.toString()
|
|
@@ -188,6 +194,12 @@ export function bigIntReplacer(_key: string, value: unknown): unknown {
|
|
|
188
194
|
return value
|
|
189
195
|
}
|
|
190
196
|
|
|
197
|
+
/**
|
|
198
|
+
* JSON reviver function that converts numeric strings back to BigInt.
|
|
199
|
+
* @param _key - Property key (unused).
|
|
200
|
+
* @param value - Value to transform.
|
|
201
|
+
* @returns BigInt if numeric string, otherwise unchanged value.
|
|
202
|
+
*/
|
|
191
203
|
export function bigIntReviver(_key: string, value: unknown): unknown {
|
|
192
204
|
if (typeof value === 'string' && /^\d+$/.test(value)) {
|
|
193
205
|
return BigInt(value)
|
|
@@ -216,15 +228,31 @@ export function decodeOnRampAddress(
|
|
|
216
228
|
return decoded
|
|
217
229
|
}
|
|
218
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Converts little-endian bytes to BigInt.
|
|
233
|
+
* @param data - Little-endian byte data.
|
|
234
|
+
* @returns BigInt value.
|
|
235
|
+
*/
|
|
219
236
|
export function leToBigInt(data: BytesLike | readonly number[]): bigint {
|
|
220
237
|
if (Array.isArray(data)) data = new Uint8Array(data)
|
|
221
238
|
return toBigInt(getBytes(data as BytesLike).reverse())
|
|
222
239
|
}
|
|
223
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Converts a BigNumber to little-endian byte array.
|
|
243
|
+
* @param value - Numeric value to convert.
|
|
244
|
+
* @param width - Optional byte width for padding.
|
|
245
|
+
* @returns Little-endian Uint8Array.
|
|
246
|
+
*/
|
|
224
247
|
export function toLeArray(value: BigNumberish, width?: Numeric): Uint8Array {
|
|
225
|
-
return
|
|
248
|
+
return toBeArray(value, width).reverse()
|
|
226
249
|
}
|
|
227
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Checks if the given data is a valid Base64 encoded string.
|
|
253
|
+
* @param data - Data to check.
|
|
254
|
+
* @returns True if valid Base64 string.
|
|
255
|
+
*/
|
|
228
256
|
export function isBase64(data: unknown): data is string {
|
|
229
257
|
return (
|
|
230
258
|
typeof data === 'string' &&
|
|
@@ -232,6 +260,11 @@ export function isBase64(data: unknown): data is string {
|
|
|
232
260
|
)
|
|
233
261
|
}
|
|
234
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Converts various data formats to Uint8Array.
|
|
265
|
+
* @param data - Bytes, number array, or Base64 string.
|
|
266
|
+
* @returns Uint8Array representation.
|
|
267
|
+
*/
|
|
235
268
|
export function getDataBytes(data: BytesLike | readonly number[]): Uint8Array {
|
|
236
269
|
if (Array.isArray(data)) {
|
|
237
270
|
return new Uint8Array(data)
|
|
@@ -245,6 +278,11 @@ export function getDataBytes(data: BytesLike | readonly number[]): Uint8Array {
|
|
|
245
278
|
}
|
|
246
279
|
}
|
|
247
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Extracts address bytes, handling both hex and Base58 formats.
|
|
283
|
+
* @param address - Address in hex or Base58 format.
|
|
284
|
+
* @returns Address bytes as Uint8Array.
|
|
285
|
+
*/
|
|
248
286
|
export function getAddressBytes(address: BytesLike): Uint8Array {
|
|
249
287
|
let bytes: Uint8Array
|
|
250
288
|
if (isBytesLike(address)) {
|
|
@@ -295,8 +333,18 @@ export function convertKeysToCamelCase(
|
|
|
295
333
|
return converted
|
|
296
334
|
}
|
|
297
335
|
|
|
298
|
-
|
|
336
|
+
/**
|
|
337
|
+
* Promise-based sleep utility.
|
|
338
|
+
* @param ms - Duration in milliseconds.
|
|
339
|
+
* @returns Promise that resolves after the specified duration.
|
|
340
|
+
*/
|
|
341
|
+
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms).unref())
|
|
299
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Parses a typeAndVersion string into its components.
|
|
345
|
+
* @param typeAndVersion - String in format "TypeName vX.Y.Z".
|
|
346
|
+
* @returns Tuple of [type, version, original, suffix?].
|
|
347
|
+
*/
|
|
300
348
|
export function parseTypeAndVersion(
|
|
301
349
|
typeAndVersion: string,
|
|
302
350
|
): Awaited<ReturnType<Chain['typeAndVersion']>> {
|
|
@@ -305,7 +353,7 @@ export function parseTypeAndVersion(
|
|
|
305
353
|
throw new Error(
|
|
306
354
|
`Invalid typeAndVersion: "${typeAndVersion}", len=${typeAndVersion.length}, hex=0x${Buffer.from(typeAndVersion).toString('hex')}`,
|
|
307
355
|
)
|
|
308
|
-
const [
|
|
356
|
+
const [, typeRaw, version] = match
|
|
309
357
|
// some string normalization
|
|
310
358
|
const type = typeRaw
|
|
311
359
|
.replaceAll(/-(\w)/g, (_, w: string) => w.toUpperCase()) // kebabToPascal
|
|
@@ -319,14 +367,26 @@ export function parseTypeAndVersion(
|
|
|
319
367
|
else return [type, version, typeAndVersion, match[3]]
|
|
320
368
|
}
|
|
321
369
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Creates a rate-limited fetch function with retry logic.
|
|
372
|
+
* Configurable via maxRequests, windowMs, and maxRetries options.
|
|
373
|
+
* @returns Rate-limited fetch function.
|
|
374
|
+
*/
|
|
375
|
+
export function createRateLimitedFetch(
|
|
376
|
+
{
|
|
377
|
+
maxRequests = 40,
|
|
378
|
+
windowMs = 11e3,
|
|
379
|
+
maxRetries = 5,
|
|
380
|
+
}: { maxRequests?: number; windowMs?: number; maxRetries?: number } = {},
|
|
381
|
+
{ logger = console }: WithLogger = {},
|
|
382
|
+
): typeof fetch {
|
|
327
383
|
// Custom fetch implementation with retry logic and rate limiting
|
|
328
384
|
// Per-instance state
|
|
329
385
|
const requestQueue: Array<{ timestamp: number }> = []
|
|
386
|
+
const methodRateLimits: Record<
|
|
387
|
+
string,
|
|
388
|
+
{ limit: number; remaining: number; queue: Array<{ timestamp: number }> }
|
|
389
|
+
> = {}
|
|
330
390
|
|
|
331
391
|
const isRateLimited = (): boolean => {
|
|
332
392
|
const now = Date.now()
|
|
@@ -337,7 +397,31 @@ export function createRateLimitedFetch({
|
|
|
337
397
|
return requestQueue.length >= maxRequests
|
|
338
398
|
}
|
|
339
399
|
|
|
340
|
-
const
|
|
400
|
+
const isMethodRateLimited = (method: string): boolean => {
|
|
401
|
+
const methodLimit = methodRateLimits[method]
|
|
402
|
+
if (!methodLimit) return false
|
|
403
|
+
|
|
404
|
+
const now = Date.now()
|
|
405
|
+
// Remove old requests outside the window
|
|
406
|
+
while (methodLimit.queue.length > 0 && now - methodLimit.queue[0].timestamp > windowMs) {
|
|
407
|
+
methodLimit.queue.shift()
|
|
408
|
+
}
|
|
409
|
+
return methodLimit.queue.length >= methodLimit.limit
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const waitForRateLimit = async (method?: string): Promise<void> => {
|
|
413
|
+
// Wait for method-specific rate limit if applicable
|
|
414
|
+
if (method && methodRateLimits[method]) {
|
|
415
|
+
while (isMethodRateLimited(method)) {
|
|
416
|
+
const oldestRequest = methodRateLimits[method].queue[0]
|
|
417
|
+
const waitTime = windowMs - (Date.now() - oldestRequest.timestamp)
|
|
418
|
+
if (waitTime > 0) {
|
|
419
|
+
await sleep(waitTime + 100) // Add small buffer
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Wait for global rate limit
|
|
341
425
|
while (isRateLimited()) {
|
|
342
426
|
const oldestRequest = requestQueue[0]
|
|
343
427
|
const waitTime = windowMs - (Date.now() - oldestRequest.timestamp)
|
|
@@ -347,8 +431,39 @@ export function createRateLimitedFetch({
|
|
|
347
431
|
}
|
|
348
432
|
}
|
|
349
433
|
|
|
350
|
-
const recordRequest = (): void => {
|
|
351
|
-
|
|
434
|
+
const recordRequest = (method?: string): void => {
|
|
435
|
+
const timestamp = Date.now()
|
|
436
|
+
requestQueue.push({ timestamp })
|
|
437
|
+
if (method && methodRateLimits[method]) {
|
|
438
|
+
methodRateLimits[method].queue.push({ timestamp })
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const updateMethodRateLimits = (response: Response, method?: string): void => {
|
|
443
|
+
if (!method) return
|
|
444
|
+
|
|
445
|
+
const limit = Number(response.headers.get('x-ratelimit-method-limit'))
|
|
446
|
+
const remaining = Number(response.headers.get('x-ratelimit-method-remaining'))
|
|
447
|
+
|
|
448
|
+
if (isNaN(limit) || isNaN(remaining)) return
|
|
449
|
+
if (!methodRateLimits[method]) {
|
|
450
|
+
methodRateLimits[method] = { limit, remaining, queue: [] }
|
|
451
|
+
} else {
|
|
452
|
+
methodRateLimits[method].limit = limit
|
|
453
|
+
methodRateLimits[method].remaining = remaining
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const extractMethod = (init?: RequestInit): string | undefined => {
|
|
458
|
+
if (!init?.body || (typeof init.body !== 'string' && typeof init.body !== 'object')) return
|
|
459
|
+
try {
|
|
460
|
+
const parsed = (typeof init.body === 'string' ? JSON.parse(init.body) : init.body) as {
|
|
461
|
+
method?: string
|
|
462
|
+
}
|
|
463
|
+
if (parsed && typeof parsed.method === 'string') return parsed.method
|
|
464
|
+
} catch {
|
|
465
|
+
// Not JSON or no method field
|
|
466
|
+
}
|
|
352
467
|
}
|
|
353
468
|
|
|
354
469
|
const isRateLimitError = (error: unknown): boolean => {
|
|
@@ -360,19 +475,23 @@ export function createRateLimitedFetch({
|
|
|
360
475
|
|
|
361
476
|
return async (input, init?) => {
|
|
362
477
|
let lastError: Error | null = null
|
|
478
|
+
const method = extractMethod(init)
|
|
363
479
|
|
|
364
480
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
365
481
|
try {
|
|
366
482
|
// Wait for rate limit before making request
|
|
367
|
-
await waitForRateLimit()
|
|
368
|
-
recordRequest()
|
|
369
|
-
//
|
|
483
|
+
await waitForRateLimit(method)
|
|
484
|
+
recordRequest(method)
|
|
485
|
+
// logger.debug('__fetching', input, init?.body)
|
|
370
486
|
|
|
371
487
|
const response = await fetch(input, init)
|
|
372
488
|
|
|
489
|
+
// Update method rate limits from response headers
|
|
490
|
+
updateMethodRateLimits(response, method)
|
|
491
|
+
|
|
373
492
|
// If response is successful, return it
|
|
374
493
|
if (response.ok) {
|
|
375
|
-
|
|
494
|
+
logger.debug('fetched', input, response.status, init?.body)
|
|
376
495
|
return response
|
|
377
496
|
}
|
|
378
497
|
|
|
@@ -382,10 +501,10 @@ export function createRateLimitedFetch({
|
|
|
382
501
|
}
|
|
383
502
|
|
|
384
503
|
// For other non-2xx responses, don't retry
|
|
385
|
-
|
|
504
|
+
logger.debug('fetch non-retryable error', input, response.status, init?.body)
|
|
386
505
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
387
506
|
} catch (error) {
|
|
388
|
-
|
|
507
|
+
logger.debug('fetch errored', attempt, error, input, init?.body)
|
|
389
508
|
lastError = error instanceof Error ? error : new Error(String(error))
|
|
390
509
|
|
|
391
510
|
// Only retry on rate limit errors
|
|
@@ -403,3 +522,26 @@ export function createRateLimitedFetch({
|
|
|
403
522
|
throw lastError || new Error('Request failed after all retries')
|
|
404
523
|
}
|
|
405
524
|
}
|
|
525
|
+
|
|
526
|
+
// barebones `node:util` backfill, if needed
|
|
527
|
+
const util =
|
|
528
|
+
'util' in globalThis
|
|
529
|
+
? (
|
|
530
|
+
globalThis as unknown as {
|
|
531
|
+
util: {
|
|
532
|
+
inspect: ((v: unknown) => string) & {
|
|
533
|
+
custom: symbol
|
|
534
|
+
defaultOptions: Record<string, unknown>
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
).util
|
|
539
|
+
: {
|
|
540
|
+
inspect: Object.assign((v: unknown) => JSON.stringify(v), {
|
|
541
|
+
custom: Symbol('custom'),
|
|
542
|
+
defaultOptions: {
|
|
543
|
+
depth: 2,
|
|
544
|
+
} as Record<string, unknown>,
|
|
545
|
+
}),
|
|
546
|
+
}
|
|
547
|
+
export { util }
|