@chainlink/ccip-sdk 0.0.0 → 0.90.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/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/aptos/exec.d.ts +18 -0
- package/dist/aptos/exec.d.ts.map +1 -0
- package/dist/aptos/exec.js +55 -0
- package/dist/aptos/exec.js.map +1 -0
- package/dist/aptos/hasher.d.ts +11 -0
- package/dist/aptos/hasher.d.ts.map +1 -0
- package/dist/aptos/hasher.js +62 -0
- package/dist/aptos/hasher.js.map +1 -0
- package/dist/aptos/index.d.ts +92 -0
- package/dist/aptos/index.d.ts.map +1 -0
- package/dist/aptos/index.js +482 -0
- package/dist/aptos/index.js.map +1 -0
- package/dist/aptos/logs.d.ts +9 -0
- package/dist/aptos/logs.d.ts.map +1 -0
- package/dist/aptos/logs.js +167 -0
- package/dist/aptos/logs.js.map +1 -0
- package/dist/aptos/send.d.ts +11 -0
- package/dist/aptos/send.d.ts.map +1 -0
- package/dist/aptos/send.js +78 -0
- package/dist/aptos/send.js.map +1 -0
- package/dist/aptos/token.d.ts +4 -0
- package/dist/aptos/token.d.ts.map +1 -0
- package/dist/aptos/token.js +134 -0
- package/dist/aptos/token.js.map +1 -0
- package/dist/aptos/types.d.ts +78 -0
- package/dist/aptos/types.d.ts.map +1 -0
- package/dist/aptos/types.js +60 -0
- package/dist/aptos/types.js.map +1 -0
- package/dist/aptos/utils.d.ts +12 -0
- package/dist/aptos/utils.d.ts.map +1 -0
- package/dist/aptos/utils.js +15 -0
- package/dist/aptos/utils.js.map +1 -0
- package/dist/chain.d.ts +344 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +41 -0
- package/dist/chain.js.map +1 -0
- package/dist/commits.d.ts +25 -0
- package/dist/commits.d.ts.map +1 -0
- package/dist/commits.js +29 -0
- package/dist/commits.js.map +1 -0
- package/dist/evm/abi/BurnMintERC677Token.d.ts +602 -0
- package/dist/evm/abi/BurnMintERC677Token.d.ts.map +1 -0
- package/dist/evm/abi/BurnMintERC677Token.js +488 -0
- package/dist/evm/abi/BurnMintERC677Token.js.map +1 -0
- package/dist/evm/abi/CommitStore_1_2.d.ts +688 -0
- package/dist/evm/abi/CommitStore_1_2.d.ts.map +1 -0
- package/dist/evm/abi/CommitStore_1_2.js +638 -0
- package/dist/evm/abi/CommitStore_1_2.js.map +1 -0
- package/dist/evm/abi/CommitStore_1_5.d.ts +708 -0
- package/dist/evm/abi/CommitStore_1_5.d.ts.map +1 -0
- package/dist/evm/abi/CommitStore_1_5.js +675 -0
- package/dist/evm/abi/CommitStore_1_5.js.map +1 -0
- package/dist/evm/abi/FeeQuoter_1_6.d.ts +1770 -0
- package/dist/evm/abi/FeeQuoter_1_6.d.ts.map +1 -0
- package/dist/evm/abi/FeeQuoter_1_6.js +1904 -0
- package/dist/evm/abi/FeeQuoter_1_6.js.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5.d.ts +1116 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5.d.ts.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5.js +1096 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5.js.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5_1.d.ts +1306 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5_1.d.ts.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5_1.js +1278 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_5_1.js.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_6_1.d.ts +1290 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_6_1.d.ts.map +1 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_6_1.js +1288 -0
- package/dist/evm/abi/LockReleaseTokenPool_1_6_1.js.map +1 -0
- package/dist/evm/abi/OffRamp_1_2.d.ts +1217 -0
- package/dist/evm/abi/OffRamp_1_2.d.ts.map +1 -0
- package/dist/evm/abi/OffRamp_1_2.js +1204 -0
- package/dist/evm/abi/OffRamp_1_2.js.map +1 -0
- package/dist/evm/abi/OffRamp_1_5.d.ts +1271 -0
- package/dist/evm/abi/OffRamp_1_5.d.ts.map +1 -0
- package/dist/evm/abi/OffRamp_1_5.js +1273 -0
- package/dist/evm/abi/OffRamp_1_5.js.map +1 -0
- package/dist/evm/abi/OffRamp_1_6.d.ts +1472 -0
- package/dist/evm/abi/OffRamp_1_6.d.ts.map +1 -0
- package/dist/evm/abi/OffRamp_1_6.js +1529 -0
- package/dist/evm/abi/OffRamp_1_6.js.map +1 -0
- package/dist/evm/abi/OnRamp_1_2.d.ts +1391 -0
- package/dist/evm/abi/OnRamp_1_2.d.ts.map +1 -0
- package/dist/evm/abi/OnRamp_1_2.js +1343 -0
- package/dist/evm/abi/OnRamp_1_2.js.map +1 -0
- package/dist/evm/abi/OnRamp_1_5.d.ts +1443 -0
- package/dist/evm/abi/OnRamp_1_5.d.ts.map +1 -0
- package/dist/evm/abi/OnRamp_1_5.js +1427 -0
- package/dist/evm/abi/OnRamp_1_5.js.map +1 -0
- package/dist/evm/abi/OnRamp_1_6.d.ts +796 -0
- package/dist/evm/abi/OnRamp_1_6.d.ts.map +1 -0
- package/dist/evm/abi/OnRamp_1_6.js +880 -0
- package/dist/evm/abi/OnRamp_1_6.js.map +1 -0
- package/dist/evm/abi/Router.d.ts +541 -0
- package/dist/evm/abi/Router.d.ts.map +1 -0
- package/dist/evm/abi/Router.js +508 -0
- package/dist/evm/abi/Router.js.map +1 -0
- package/dist/evm/abi/TokenAdminRegistry_1_5.d.ts +373 -0
- package/dist/evm/abi/TokenAdminRegistry_1_5.d.ts.map +1 -0
- package/dist/evm/abi/TokenAdminRegistry_1_5.js +333 -0
- package/dist/evm/abi/TokenAdminRegistry_1_5.js.map +1 -0
- package/dist/evm/const.d.ts +27 -0
- package/dist/evm/const.d.ts.map +1 -0
- package/dist/evm/const.js +63 -0
- package/dist/evm/const.js.map +1 -0
- package/dist/evm/errors.d.ts +36 -0
- package/dist/evm/errors.d.ts.map +1 -0
- package/dist/evm/errors.js +192 -0
- package/dist/evm/errors.js.map +1 -0
- package/dist/evm/hasher.d.ts +5 -0
- package/dist/evm/hasher.d.ts.map +1 -0
- package/dist/evm/hasher.js +116 -0
- package/dist/evm/hasher.js.map +1 -0
- package/dist/evm/index.d.ts +121 -0
- package/dist/evm/index.d.ts.map +1 -0
- package/dist/evm/index.js +904 -0
- package/dist/evm/index.js.map +1 -0
- package/dist/evm/messages.d.ts +35 -0
- package/dist/evm/messages.d.ts.map +1 -0
- package/dist/evm/messages.js +11 -0
- package/dist/evm/messages.js.map +1 -0
- package/dist/evm/offchain.d.ts +16 -0
- package/dist/evm/offchain.d.ts.map +1 -0
- package/dist/evm/offchain.js +142 -0
- package/dist/evm/offchain.js.map +1 -0
- package/dist/execution.d.ts +80 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +91 -0
- package/dist/execution.js.map +1 -0
- package/dist/extra-args.d.ts +45 -0
- package/dist/extra-args.d.ts.map +1 -0
- package/dist/extra-args.js +44 -0
- package/dist/extra-args.js.map +1 -0
- package/dist/gas.d.ts +27 -0
- package/dist/gas.d.ts.map +1 -0
- package/dist/gas.js +80 -0
- package/dist/gas.js.map +1 -0
- package/dist/hasher/common.d.ts +12 -0
- package/dist/hasher/common.d.ts.map +1 -0
- package/dist/hasher/common.js +19 -0
- package/dist/hasher/common.js.map +1 -0
- package/dist/hasher/hasher.d.ts +4 -0
- package/dist/hasher/hasher.d.ts.map +1 -0
- package/dist/hasher/hasher.js +11 -0
- package/dist/hasher/hasher.js.map +1 -0
- package/dist/hasher/index.d.ts +4 -0
- package/dist/hasher/index.d.ts.map +1 -0
- package/dist/hasher/index.js +4 -0
- package/dist/hasher/index.js.map +1 -0
- package/dist/hasher/merklemulti.d.ts +58 -0
- package/dist/hasher/merklemulti.d.ts.map +1 -0
- package/dist/hasher/merklemulti.js +257 -0
- package/dist/hasher/merklemulti.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/offchain.d.ts +20 -0
- package/dist/offchain.d.ts.map +1 -0
- package/dist/offchain.js +59 -0
- package/dist/offchain.js.map +1 -0
- package/dist/requests.d.ts +48 -0
- package/dist/requests.d.ts.map +1 -0
- package/dist/requests.js +286 -0
- package/dist/requests.js.map +1 -0
- package/dist/selectors.d.ts +9 -0
- package/dist/selectors.d.ts.map +1 -0
- package/dist/selectors.js +1330 -0
- package/dist/selectors.js.map +1 -0
- package/dist/solana/cleanup.d.ts +15 -0
- package/dist/solana/cleanup.d.ts.map +1 -0
- package/dist/solana/cleanup.js +159 -0
- package/dist/solana/cleanup.js.map +1 -0
- package/dist/solana/exec.d.ts +15 -0
- package/dist/solana/exec.d.ts.map +1 -0
- package/dist/solana/exec.js +417 -0
- package/dist/solana/exec.js.map +1 -0
- package/dist/solana/hasher.d.ts +4 -0
- package/dist/solana/hasher.d.ts.map +1 -0
- package/dist/solana/hasher.js +81 -0
- package/dist/solana/hasher.js.map +1 -0
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts +866 -0
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js +866 -0
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js.map +1 -0
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts +949 -0
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js +949 -0
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts +1374 -0
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js +1374 -0
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts +104 -0
- package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_COMMON.js +104 -0
- package/dist/solana/idl/1.6.0/CCIP_COMMON.js.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts +2746 -0
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js +2746 -0
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts +2332 -0
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts.map +1 -0
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.js +2332 -0
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.js.map +1 -0
- package/dist/solana/index.d.ts +205 -0
- package/dist/solana/index.d.ts.map +1 -0
- package/dist/solana/index.js +1085 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/solana/offchain.d.ts +31 -0
- package/dist/solana/offchain.d.ts.map +1 -0
- package/dist/solana/offchain.js +152 -0
- package/dist/solana/offchain.js.map +1 -0
- package/dist/solana/patchBorsh.d.ts +2 -0
- package/dist/solana/patchBorsh.d.ts.map +1 -0
- package/dist/solana/patchBorsh.js +60 -0
- package/dist/solana/patchBorsh.js.map +1 -0
- package/dist/solana/send.d.ts +14 -0
- package/dist/solana/send.d.ts.map +1 -0
- package/dist/solana/send.js +272 -0
- package/dist/solana/send.js.map +1 -0
- package/dist/solana/types.d.ts +4 -0
- package/dist/solana/types.d.ts.map +1 -0
- package/dist/solana/types.js +2 -0
- package/dist/solana/types.js.map +1 -0
- package/dist/solana/utils.d.ts +58 -0
- package/dist/solana/utils.d.ts.map +1 -0
- package/dist/solana/utils.js +211 -0
- package/dist/solana/utils.js.map +1 -0
- package/dist/sui/hasher.d.ts +12 -0
- package/dist/sui/hasher.d.ts.map +1 -0
- package/dist/sui/hasher.js +63 -0
- package/dist/sui/hasher.js.map +1 -0
- package/dist/sui/index.d.ts +72 -0
- package/dist/sui/index.d.ts.map +1 -0
- package/dist/sui/index.js +128 -0
- package/dist/sui/index.js.map +1 -0
- package/dist/sui/types.d.ts +17 -0
- package/dist/sui/types.d.ts.map +1 -0
- package/dist/sui/types.js +17 -0
- package/dist/sui/types.js.map +1 -0
- package/dist/supported-chains.d.ts +5 -0
- package/dist/supported-chains.d.ts.map +1 -0
- package/dist/supported-chains.js +3 -0
- package/dist/supported-chains.js.map +1 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +117 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +336 -0
- package/dist/utils.js.map +1 -0
- package/package.json +66 -8
- package/src/aptos/exec.ts +69 -0
- package/src/aptos/hasher.ts +92 -0
- package/src/aptos/index.ts +660 -0
- package/src/aptos/logs.ts +210 -0
- package/src/aptos/send.ts +120 -0
- package/src/aptos/token.ts +150 -0
- package/src/aptos/types.ts +85 -0
- package/src/aptos/utils.ts +24 -0
- package/src/chain.ts +398 -0
- package/src/commits.ts +44 -0
- package/src/evm/abi/BurnMintERC677Token.ts +487 -0
- package/src/evm/abi/CommitStore_1_2.ts +637 -0
- package/src/evm/abi/CommitStore_1_5.ts +674 -0
- package/src/evm/abi/FeeQuoter_1_6.ts +1903 -0
- package/src/evm/abi/LockReleaseTokenPool_1_5.ts +1095 -0
- package/src/evm/abi/LockReleaseTokenPool_1_5_1.ts +1277 -0
- package/src/evm/abi/LockReleaseTokenPool_1_6_1.ts +1287 -0
- package/src/evm/abi/OffRamp_1_2.ts +1203 -0
- package/src/evm/abi/OffRamp_1_5.ts +1272 -0
- package/src/evm/abi/OffRamp_1_6.ts +1528 -0
- package/src/evm/abi/OnRamp_1_2.ts +1342 -0
- package/src/evm/abi/OnRamp_1_5.ts +1426 -0
- package/src/evm/abi/OnRamp_1_6.ts +879 -0
- package/src/evm/abi/Router.ts +507 -0
- package/src/evm/abi/TokenAdminRegistry_1_5.ts +332 -0
- package/src/evm/const.ts +69 -0
- package/src/evm/errors.ts +212 -0
- package/src/evm/hasher.ts +166 -0
- package/src/evm/index.ts +1262 -0
- package/src/evm/messages.ts +73 -0
- package/src/evm/offchain.ts +189 -0
- package/src/execution.ts +131 -0
- package/src/extra-args.ts +71 -0
- package/src/gas.ts +135 -0
- package/src/hasher/common.ts +23 -0
- package/src/hasher/hasher.ts +12 -0
- package/src/hasher/index.ts +3 -0
- package/src/hasher/merklemulti.ts +309 -0
- package/src/index.ts +51 -0
- package/src/offchain.ts +86 -0
- package/src/requests.ts +339 -0
- package/src/selectors.ts +1340 -0
- package/src/solana/cleanup.ts +216 -0
- package/src/solana/exec.ts +645 -0
- package/src/solana/hasher.ts +104 -0
- package/src/solana/idl/1.6.0/BASE_TOKEN_POOL.ts +1734 -0
- package/src/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.ts +1900 -0
- package/src/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts +2750 -0
- package/src/solana/idl/1.6.0/CCIP_COMMON.ts +210 -0
- package/src/solana/idl/1.6.0/CCIP_OFFRAMP.ts +5494 -0
- package/src/solana/idl/1.6.0/CCIP_ROUTER.ts +4671 -0
- package/src/solana/index.ts +1454 -0
- package/src/solana/offchain.ts +209 -0
- package/src/solana/patchBorsh.ts +67 -0
- package/src/solana/send.ts +436 -0
- package/src/solana/types.ts +6 -0
- package/src/solana/utils.ts +272 -0
- package/src/sui/hasher.ts +90 -0
- package/src/sui/index.ts +198 -0
- package/src/sui/types.ts +22 -0
- package/src/supported-chains.ts +4 -0
- package/src/types.ts +153 -0
- package/src/utils.ts +405 -0
- package/tsconfig.json +18 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype'
|
|
2
|
+
import type { BytesLike, Log } from 'ethers'
|
|
3
|
+
|
|
4
|
+
import type { ChainFamily, ChainTransaction } from './chain.ts'
|
|
5
|
+
import type OffRamp_1_6_ABI from './evm/abi/OffRamp_1_6.ts'
|
|
6
|
+
import type { CCIPMessage_EVM, CCIPMessage_V1_6_EVM } from './evm/messages.ts'
|
|
7
|
+
import type { ExtraArgs } from './extra-args.ts'
|
|
8
|
+
import type { CCIPMessage_V1_6_Solana } from './solana/types.ts'
|
|
9
|
+
import type { CCIPMessage_V1_6_Sui } from './sui/types.ts'
|
|
10
|
+
// v1.6 Base type from EVM contains the intersection of all other CCIPMessage v1.6 types
|
|
11
|
+
export type { CCIPMessage_V1_6 } from './evm/messages.ts'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* DeepReadonly is a type that recursively makes all properties of an object readonly.
|
|
15
|
+
*/
|
|
16
|
+
export type DeepReadonly<T> = Readonly<{
|
|
17
|
+
[K in keyof T]: T[K] extends number | string | symbol // Is it a primitive? Then make it readonly
|
|
18
|
+
? Readonly<T[K]>
|
|
19
|
+
: // Is it an array of items? Then make the array readonly and the item as well
|
|
20
|
+
T[K] extends Array<infer A>
|
|
21
|
+
? Readonly<Array<DeepReadonly<A>>>
|
|
22
|
+
: // It is some other object, make it readonly as well
|
|
23
|
+
DeepReadonly<T[K]>
|
|
24
|
+
}>
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* "Fix" for deeply intersecting types containing arrays: A[] & B[] => (A & B)[]
|
|
28
|
+
* Usually, if you intersect { arr: A[] } & { arr: B[] }, arr will have type A[] & B[],
|
|
29
|
+
* 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)[], i.e. each *element* should have all properties of A & B
|
|
32
|
+
*/
|
|
33
|
+
export type MergeArrayElements<T, U> = {
|
|
34
|
+
[K in keyof (T & U)]: K extends keyof T & keyof U
|
|
35
|
+
? T[K] extends unknown[]
|
|
36
|
+
? U[K] extends unknown[]
|
|
37
|
+
? (T[K][number] & U[K][number])[] // Intersect element types, both rw: A[] & B[] => (A & B)[]
|
|
38
|
+
: U[K] extends readonly unknown[]
|
|
39
|
+
? readonly (T[K][number] & U[K][number])[] // Intersect element types, 2nd ro
|
|
40
|
+
: never
|
|
41
|
+
: T[K] extends readonly unknown[]
|
|
42
|
+
? U[K] extends readonly unknown[]
|
|
43
|
+
? readonly (T[K][number] & U[K][number])[] // Intersect element types, 1st or both ro
|
|
44
|
+
: never
|
|
45
|
+
: U[K] extends readonly unknown[]
|
|
46
|
+
? never
|
|
47
|
+
: MergeArrayElements<T[K], U[K]> // Recurse deeper
|
|
48
|
+
: K extends keyof T
|
|
49
|
+
? T[K]
|
|
50
|
+
: K extends keyof U
|
|
51
|
+
? U[K]
|
|
52
|
+
: never
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const CCIPVersion = {
|
|
56
|
+
V1_2: '1.2.0',
|
|
57
|
+
V1_5: '1.5.0',
|
|
58
|
+
V1_6: '1.6.0',
|
|
59
|
+
} as const
|
|
60
|
+
export type CCIPVersion = (typeof CCIPVersion)[keyof typeof CCIPVersion]
|
|
61
|
+
|
|
62
|
+
type ChainFamilyWithId<F extends ChainFamily> = F extends typeof ChainFamily.EVM
|
|
63
|
+
? { readonly family: F; readonly chainId: number }
|
|
64
|
+
: F extends typeof ChainFamily.Solana
|
|
65
|
+
? { readonly family: F; readonly chainId: string }
|
|
66
|
+
: F extends typeof ChainFamily.Aptos | typeof ChainFamily.Sui
|
|
67
|
+
? { readonly family: F; readonly chainId: `${F}:${number}` }
|
|
68
|
+
: never
|
|
69
|
+
|
|
70
|
+
export type NetworkInfo<F extends ChainFamily = ChainFamily> = {
|
|
71
|
+
readonly chainSelector: bigint
|
|
72
|
+
readonly name: string
|
|
73
|
+
readonly isTestnet: boolean
|
|
74
|
+
} & ChainFamilyWithId<F>
|
|
75
|
+
|
|
76
|
+
export interface Lane<V extends CCIPVersion = CCIPVersion> {
|
|
77
|
+
sourceChainSelector: bigint
|
|
78
|
+
destChainSelector: bigint
|
|
79
|
+
onRamp: string
|
|
80
|
+
version: V
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type CCIPMessage<V extends CCIPVersion = CCIPVersion> = V extends
|
|
84
|
+
| typeof CCIPVersion.V1_2
|
|
85
|
+
| typeof CCIPVersion.V1_5
|
|
86
|
+
? CCIPMessage_EVM<V>
|
|
87
|
+
: CCIPMessage_V1_6_EVM | CCIPMessage_V1_6_Solana | CCIPMessage_V1_6_Sui
|
|
88
|
+
|
|
89
|
+
export type Log_ = Pick<Log, 'topics' | 'index' | 'address' | 'blockNumber' | 'transactionHash'> & {
|
|
90
|
+
data: BytesLike | Record<string, unknown>
|
|
91
|
+
tx?: ChainTransaction
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CCIPRequest<V extends CCIPVersion = CCIPVersion> {
|
|
95
|
+
lane: Lane<V>
|
|
96
|
+
message: CCIPMessage<V>
|
|
97
|
+
log: Log_
|
|
98
|
+
tx: { logs: readonly Log_[]; from?: string; error?: unknown }
|
|
99
|
+
timestamp: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type CommitReport = AbiParametersToPrimitiveTypes<
|
|
103
|
+
ExtractAbiEvent<typeof OffRamp_1_6_ABI, 'CommitReportAccepted'>['inputs']
|
|
104
|
+
>[0][number]
|
|
105
|
+
|
|
106
|
+
export interface CCIPCommit {
|
|
107
|
+
report: CommitReport
|
|
108
|
+
log: Log_
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const ExecutionState = {
|
|
112
|
+
InProgress: 1,
|
|
113
|
+
Success: 2,
|
|
114
|
+
Failed: 3,
|
|
115
|
+
} as const
|
|
116
|
+
export type ExecutionState = (typeof ExecutionState)[keyof typeof ExecutionState]
|
|
117
|
+
|
|
118
|
+
export type ExecutionReceipt = {
|
|
119
|
+
messageId: string
|
|
120
|
+
sequenceNumber: bigint
|
|
121
|
+
state: ExecutionState
|
|
122
|
+
sourceChainSelector?: bigint
|
|
123
|
+
messageHash?: string
|
|
124
|
+
returnData?: BytesLike | Record<string, string>
|
|
125
|
+
gasUsed?: bigint
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface CCIPExecution {
|
|
129
|
+
receipt: ExecutionReceipt
|
|
130
|
+
log: Log_
|
|
131
|
+
timestamp: number
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type OffchainTokenData = { _tag: string; [k: string]: BytesLike } | undefined
|
|
135
|
+
|
|
136
|
+
export type ExecutionReport<M extends CCIPMessage = CCIPMessage> = {
|
|
137
|
+
message: M
|
|
138
|
+
proofs: readonly BytesLike[]
|
|
139
|
+
proofFlagBits: bigint
|
|
140
|
+
merkleRoot: string
|
|
141
|
+
offchainTokenData: readonly OffchainTokenData[]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A message to be sent to another network
|
|
146
|
+
*/
|
|
147
|
+
export type AnyMessage = {
|
|
148
|
+
receiver: BytesLike
|
|
149
|
+
data: BytesLike
|
|
150
|
+
extraArgs: ExtraArgs
|
|
151
|
+
tokenAmounts?: readonly { token: string; amount: bigint }[]
|
|
152
|
+
feeToken?: string
|
|
153
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import util from 'util'
|
|
2
|
+
|
|
3
|
+
import bs58 from 'bs58'
|
|
4
|
+
import {
|
|
5
|
+
type BigNumberish,
|
|
6
|
+
type BytesLike,
|
|
7
|
+
type Numeric,
|
|
8
|
+
decodeBase64,
|
|
9
|
+
getBytes,
|
|
10
|
+
isBytesLike,
|
|
11
|
+
toBeHex,
|
|
12
|
+
toBigInt,
|
|
13
|
+
} from 'ethers'
|
|
14
|
+
import moize from 'moize'
|
|
15
|
+
|
|
16
|
+
import { type Chain, ChainFamily } from './chain.ts'
|
|
17
|
+
import SELECTORS from './selectors.ts'
|
|
18
|
+
import { supportedChains } from './supported-chains.ts'
|
|
19
|
+
import type { NetworkInfo } from './types.ts'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns *some* block number with timestamp prior to `timestamp`
|
|
23
|
+
*
|
|
24
|
+
* @param getBlockTimestamp - function to get block timestamp
|
|
25
|
+
* @param recentBlockNumber - a block guaranteed to be after `timestamp` (e.g. latest)
|
|
26
|
+
* @param timestamp - target timestamp
|
|
27
|
+
* @param precision - returned blockNumber should be within this many blocks before timestamp
|
|
28
|
+
* @returns blockNumber of a block at provider which is close but before target timestamp
|
|
29
|
+
**/
|
|
30
|
+
export async function getSomeBlockNumberBefore(
|
|
31
|
+
getBlockTimestamp: (blockNumber: number) => Promise<number>,
|
|
32
|
+
recentBlockNumber: number,
|
|
33
|
+
timestamp: number,
|
|
34
|
+
precision = 10,
|
|
35
|
+
): Promise<number> {
|
|
36
|
+
let beforeBlockNumber = Math.max(1, recentBlockNumber - precision * 1000)
|
|
37
|
+
let beforeTimestamp = await getBlockTimestamp(beforeBlockNumber)
|
|
38
|
+
|
|
39
|
+
const now = Math.trunc(Date.now() / 1000)
|
|
40
|
+
let estimatedBlockTime = (now - beforeTimestamp) / (recentBlockNumber - beforeBlockNumber),
|
|
41
|
+
afterBlockNumber = recentBlockNumber,
|
|
42
|
+
afterTimestamp = now
|
|
43
|
+
|
|
44
|
+
// first, go back looking for a block prior to our target timestamp
|
|
45
|
+
for (let iter = 0; beforeBlockNumber > 1 && beforeTimestamp > timestamp; iter++) {
|
|
46
|
+
afterBlockNumber = beforeBlockNumber
|
|
47
|
+
afterTimestamp = beforeTimestamp
|
|
48
|
+
beforeBlockNumber = Math.max(
|
|
49
|
+
1,
|
|
50
|
+
Math.trunc(beforeBlockNumber - (beforeTimestamp - timestamp) / estimatedBlockTime) -
|
|
51
|
+
10 ** iter,
|
|
52
|
+
)
|
|
53
|
+
beforeTimestamp = await getBlockTimestamp(beforeBlockNumber)
|
|
54
|
+
estimatedBlockTime = (now - beforeTimestamp) / (recentBlockNumber - beforeBlockNumber)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (beforeTimestamp > timestamp) {
|
|
58
|
+
throw new Error(`Could not find a block prior to timestamp=${timestamp}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// now, bin-search based on timestamp proportions, looking for
|
|
62
|
+
// a block at most N estimated blockTimes from our target timestamp
|
|
63
|
+
while (timestamp - beforeTimestamp >= 1 && afterBlockNumber - beforeBlockNumber > precision) {
|
|
64
|
+
const prop = (timestamp - beforeTimestamp) / (afterTimestamp - beforeTimestamp)
|
|
65
|
+
const delta =
|
|
66
|
+
prop > 0.5
|
|
67
|
+
? Math.floor(prop * (afterBlockNumber - beforeBlockNumber))
|
|
68
|
+
: Math.ceil(prop * (afterBlockNumber - beforeBlockNumber))
|
|
69
|
+
let pivot = beforeBlockNumber + delta
|
|
70
|
+
if (pivot === afterBlockNumber) {
|
|
71
|
+
pivot--
|
|
72
|
+
}
|
|
73
|
+
const pivotTimestamp = await getBlockTimestamp(pivot)
|
|
74
|
+
if (pivotTimestamp > timestamp) {
|
|
75
|
+
afterBlockNumber = pivot
|
|
76
|
+
afterTimestamp = pivotTimestamp
|
|
77
|
+
} else {
|
|
78
|
+
beforeBlockNumber = pivot
|
|
79
|
+
beforeTimestamp = pivotTimestamp
|
|
80
|
+
}
|
|
81
|
+
console.debug('getSomeBlockNumberBefore: searching block before', {
|
|
82
|
+
beforeBlockNumber,
|
|
83
|
+
beforeTimestamp,
|
|
84
|
+
pivot,
|
|
85
|
+
pivotTimestamp,
|
|
86
|
+
afterBlockNumber,
|
|
87
|
+
afterTimestamp,
|
|
88
|
+
estimatedBlockTime,
|
|
89
|
+
timestamp,
|
|
90
|
+
diffNumber: afterBlockNumber - beforeBlockNumber,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
return beforeBlockNumber
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// memoized so we always output the same object for a given chainId
|
|
97
|
+
const networkInfoFromChainId = moize.default((chainId: NetworkInfo['chainId']): NetworkInfo => {
|
|
98
|
+
const sel = SELECTORS[chainId]
|
|
99
|
+
if (!sel?.name) throw new Error(`Chain not found: ${chainId}`)
|
|
100
|
+
return {
|
|
101
|
+
chainId: isNaN(+chainId) ? chainId : +chainId,
|
|
102
|
+
chainSelector: sel.selector,
|
|
103
|
+
name: sel.name,
|
|
104
|
+
family: sel.family,
|
|
105
|
+
isTestnet: !sel.name.includes('-mainnet'),
|
|
106
|
+
} as NetworkInfo
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a chain selector, chain ID, or chain name to complete network information
|
|
111
|
+
*
|
|
112
|
+
* @param selectorOrIdOrName - Can be:
|
|
113
|
+
* - Chain selector as bigint or numeric string
|
|
114
|
+
* - Chain ID as number, bigint or string (EVM: "1", Aptos: "aptos:1", Solana: genesisHash)
|
|
115
|
+
* - Chain name as string ("ethereum-mainnet")
|
|
116
|
+
* @returns Complete NetworkInfo object
|
|
117
|
+
*/
|
|
118
|
+
export const networkInfo = moize.default(function networkInfo_(
|
|
119
|
+
selectorOrIdOrName: bigint | number | string,
|
|
120
|
+
): NetworkInfo {
|
|
121
|
+
let chainId
|
|
122
|
+
if (typeof selectorOrIdOrName === 'number') {
|
|
123
|
+
chainId = selectorOrIdOrName
|
|
124
|
+
} else if (typeof selectorOrIdOrName === 'string' && selectorOrIdOrName.match(/^\d+$/)) {
|
|
125
|
+
selectorOrIdOrName = BigInt(selectorOrIdOrName)
|
|
126
|
+
}
|
|
127
|
+
if (typeof selectorOrIdOrName === 'bigint') {
|
|
128
|
+
// maybe we got a number deserialized as bigint
|
|
129
|
+
if (selectorOrIdOrName.toString() in SELECTORS) {
|
|
130
|
+
chainId = Number(selectorOrIdOrName)
|
|
131
|
+
} else {
|
|
132
|
+
for (const id in SELECTORS) {
|
|
133
|
+
if (SELECTORS[id].selector === selectorOrIdOrName) {
|
|
134
|
+
chainId = id
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!chainId) throw new Error(`Selector not found: ${selectorOrIdOrName}`)
|
|
139
|
+
}
|
|
140
|
+
} else if (typeof selectorOrIdOrName === 'string') {
|
|
141
|
+
if (selectorOrIdOrName.includes('-')) {
|
|
142
|
+
for (const id in SELECTORS) {
|
|
143
|
+
if (SELECTORS[id].name === selectorOrIdOrName) {
|
|
144
|
+
chainId = id
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
chainId ??= selectorOrIdOrName
|
|
150
|
+
}
|
|
151
|
+
return networkInfoFromChainId(chainId as string | number)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const BLOCK_RANGE = 10_000
|
|
155
|
+
/**
|
|
156
|
+
* Generates exclusive block ranges [fromBlock, toBlock]
|
|
157
|
+
* If startBlock is given, moves forward from there (up to latestBlock),
|
|
158
|
+
* Otherwise, moves backwards down to genesis (you probably want to break/return before that)
|
|
159
|
+
**/
|
|
160
|
+
export function* blockRangeGenerator(
|
|
161
|
+
params: { page?: number } & ({ endBlock: number; startBlock?: number } | { singleBlock: number }),
|
|
162
|
+
) {
|
|
163
|
+
const stepSize = params.page ?? BLOCK_RANGE
|
|
164
|
+
if ('singleBlock' in params) {
|
|
165
|
+
yield { fromBlock: params.singleBlock, toBlock: params.singleBlock }
|
|
166
|
+
} else if ('startBlock' in params && params.startBlock) {
|
|
167
|
+
for (let fromBlock = params.startBlock; fromBlock < params.endBlock; fromBlock += stepSize) {
|
|
168
|
+
yield {
|
|
169
|
+
fromBlock,
|
|
170
|
+
toBlock: Math.min(params.endBlock, fromBlock + stepSize - 1),
|
|
171
|
+
progress: `${Math.trunc(((fromBlock - params.startBlock) / (params.endBlock - params.startBlock)) * 10000) / 100}%`,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
for (let toBlock = params.endBlock; toBlock > 1; toBlock -= stepSize) {
|
|
176
|
+
yield {
|
|
177
|
+
fromBlock: Math.max(1, toBlock - stepSize + 1),
|
|
178
|
+
toBlock,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function bigIntReplacer(_key: string, value: unknown): unknown {
|
|
185
|
+
if (typeof value === 'bigint') {
|
|
186
|
+
return value.toString()
|
|
187
|
+
}
|
|
188
|
+
return value
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function bigIntReviver(_key: string, value: unknown): unknown {
|
|
192
|
+
if (typeof value === 'string' && /^\d+$/.test(value)) {
|
|
193
|
+
return BigInt(value)
|
|
194
|
+
}
|
|
195
|
+
return value
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Decode address from a 32-byte hex string
|
|
200
|
+
**/
|
|
201
|
+
export function decodeAddress(address: BytesLike, family: ChainFamily = ChainFamily.EVM): string {
|
|
202
|
+
const chain = supportedChains[family]
|
|
203
|
+
if (!chain) throw new Error(`Unsupported chain family: ${family}`)
|
|
204
|
+
return chain.getAddress(getAddressBytes(address))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Version of decodeAddress which is aware of custom cross-chain OnRamp formats
|
|
209
|
+
**/
|
|
210
|
+
export function decodeOnRampAddress(
|
|
211
|
+
address: BytesLike,
|
|
212
|
+
family: ChainFamily = ChainFamily.EVM,
|
|
213
|
+
): string {
|
|
214
|
+
let decoded = decodeAddress(address, family)
|
|
215
|
+
if (family === ChainFamily.Aptos) decoded += '::onramp'
|
|
216
|
+
return decoded
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function leToBigInt(data: BytesLike | readonly number[]): bigint {
|
|
220
|
+
if (Array.isArray(data)) data = new Uint8Array(data)
|
|
221
|
+
return toBigInt(getBytes(data as BytesLike).reverse())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function toLeArray(value: BigNumberish, width?: Numeric): Uint8Array {
|
|
225
|
+
return getBytes(toBeHex(value, width)).reverse()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function isBase64(data: unknown): data is string {
|
|
229
|
+
return (
|
|
230
|
+
typeof data === 'string' &&
|
|
231
|
+
/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/.test(data)
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function getDataBytes(data: BytesLike | readonly number[]): Uint8Array {
|
|
236
|
+
if (Array.isArray(data)) {
|
|
237
|
+
return new Uint8Array(data)
|
|
238
|
+
}
|
|
239
|
+
if (isBytesLike(data)) {
|
|
240
|
+
return getBytes(data)
|
|
241
|
+
} else if (isBase64(data)) {
|
|
242
|
+
return decodeBase64(data)
|
|
243
|
+
} else {
|
|
244
|
+
throw new Error(`Unsupported data format: ${util.inspect(data)}`)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function getAddressBytes(address: BytesLike): Uint8Array {
|
|
249
|
+
let bytes: Uint8Array
|
|
250
|
+
if (isBytesLike(address)) {
|
|
251
|
+
bytes = getBytes(address)
|
|
252
|
+
} else {
|
|
253
|
+
bytes = bs58.decode(address)
|
|
254
|
+
}
|
|
255
|
+
if (bytes.length > 20) {
|
|
256
|
+
if (
|
|
257
|
+
bytes.slice(0, bytes.length - 20).every((b) => b === 0) &&
|
|
258
|
+
bytes.slice(-20).some((b) => b !== 0)
|
|
259
|
+
) {
|
|
260
|
+
bytes = bytes.slice(-20)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return bytes
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Converts snake_case strings to camelCase
|
|
268
|
+
*/
|
|
269
|
+
export function snakeToCamel(str: string): string {
|
|
270
|
+
return str.replace(/_([a-zA-Z])/g, (_, letter: string) => letter.toUpperCase())
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Recursively converts all snake_case keys in an object to camelCase
|
|
275
|
+
* Only converts keys that actually have snake_case format
|
|
276
|
+
*/
|
|
277
|
+
export function convertKeysToCamelCase(
|
|
278
|
+
obj: unknown,
|
|
279
|
+
mapValues?: (value: unknown, key?: string) => unknown,
|
|
280
|
+
key?: string,
|
|
281
|
+
): unknown {
|
|
282
|
+
if (Array.isArray(obj)) {
|
|
283
|
+
return obj.map((v) => convertKeysToCamelCase(v, mapValues, key))
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (obj == null || typeof obj !== 'object') return mapValues ? mapValues(obj, key) : obj
|
|
287
|
+
|
|
288
|
+
const record = obj as Record<string, unknown>
|
|
289
|
+
const converted: Record<string, unknown> = {}
|
|
290
|
+
|
|
291
|
+
for (const [name, value] of Object.entries(record)) {
|
|
292
|
+
const camelKey = snakeToCamel(name)
|
|
293
|
+
converted[camelKey] = convertKeysToCamelCase(value, mapValues, camelKey)
|
|
294
|
+
}
|
|
295
|
+
return converted
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
299
|
+
|
|
300
|
+
export function parseTypeAndVersion(
|
|
301
|
+
typeAndVersion: string,
|
|
302
|
+
): Awaited<ReturnType<Chain['typeAndVersion']>> {
|
|
303
|
+
const match = typeAndVersion.match(/^(\w.+\S)\s+v?(\d+\.\d+(?:\.\d+)?)([^\d.].*)?$/)
|
|
304
|
+
if (!match)
|
|
305
|
+
throw new Error(
|
|
306
|
+
`Invalid typeAndVersion: "${typeAndVersion}", len=${typeAndVersion.length}, hex=0x${Buffer.from(typeAndVersion).toString('hex')}`,
|
|
307
|
+
)
|
|
308
|
+
const [_, typeRaw, version] = match
|
|
309
|
+
// some string normalization
|
|
310
|
+
const type = typeRaw
|
|
311
|
+
.replaceAll(/-(\w)/g, (_, w: string) => w.toUpperCase()) // kebabToPascal
|
|
312
|
+
.replace(/ccip/gi, 'CCIP')
|
|
313
|
+
.replace(
|
|
314
|
+
/(o)(n|ff)(ramp)\b/gi,
|
|
315
|
+
(_, o: string, n: string, ramp: string) =>
|
|
316
|
+
`${o.toUpperCase()}${n.toLowerCase()}${ramp.charAt(0).toUpperCase()}${ramp.slice(1).toLowerCase()}`,
|
|
317
|
+
) // ccipOfframp -> CCIPOffRamp
|
|
318
|
+
if (!match[3]) return [type, version, typeAndVersion]
|
|
319
|
+
else return [type, version, typeAndVersion, match[3]]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function createRateLimitedFetch({
|
|
323
|
+
maxRequests = Number(process.env['RL_MAX_REQUESTS'] || 2),
|
|
324
|
+
windowMs = Number(process.env['RL_WINDOW_MS'] || 10000),
|
|
325
|
+
maxRetries = Number(process.env['RL_MAX_RETRIES'] || 5),
|
|
326
|
+
}: { maxRequests?: number; windowMs?: number; maxRetries?: number } = {}): typeof fetch {
|
|
327
|
+
// Custom fetch implementation with retry logic and rate limiting
|
|
328
|
+
// Per-instance state
|
|
329
|
+
const requestQueue: Array<{ timestamp: number }> = []
|
|
330
|
+
|
|
331
|
+
const isRateLimited = (): boolean => {
|
|
332
|
+
const now = Date.now()
|
|
333
|
+
// Remove old requests outside the window
|
|
334
|
+
while (requestQueue.length > 0 && now - requestQueue[0].timestamp > windowMs) {
|
|
335
|
+
requestQueue.shift()
|
|
336
|
+
}
|
|
337
|
+
return requestQueue.length >= maxRequests
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const waitForRateLimit = async (): Promise<void> => {
|
|
341
|
+
while (isRateLimited()) {
|
|
342
|
+
const oldestRequest = requestQueue[0]
|
|
343
|
+
const waitTime = windowMs - (Date.now() - oldestRequest.timestamp)
|
|
344
|
+
if (waitTime > 0) {
|
|
345
|
+
await sleep(waitTime + 100) // Add small buffer
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const recordRequest = (): void => {
|
|
351
|
+
requestQueue.push({ timestamp: Date.now() })
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const isRateLimitError = (error: unknown): boolean => {
|
|
355
|
+
if (error instanceof Error) {
|
|
356
|
+
return !!error.message.match(/\b(429\b|rate.?limit)/i)
|
|
357
|
+
}
|
|
358
|
+
return false
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return async (input, init?) => {
|
|
362
|
+
let lastError: Error | null = null
|
|
363
|
+
|
|
364
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
365
|
+
try {
|
|
366
|
+
// Wait for rate limit before making request
|
|
367
|
+
await waitForRateLimit()
|
|
368
|
+
recordRequest()
|
|
369
|
+
// console.debug('__fetching', input, init?.body)
|
|
370
|
+
|
|
371
|
+
const response = await fetch(input, init)
|
|
372
|
+
|
|
373
|
+
// If response is successful, return it
|
|
374
|
+
if (response.ok) {
|
|
375
|
+
console.debug('fetched', input, response.status, init?.body)
|
|
376
|
+
return response
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// For 429 responses, throw an error to trigger retry
|
|
380
|
+
if (response.status === 429) {
|
|
381
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// For other non-2xx responses, don't retry
|
|
385
|
+
console.debug('fetch non-retryable error', input, response.status, init?.body)
|
|
386
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.debug('fetch errored', attempt, error, input, init?.body)
|
|
389
|
+
lastError = error instanceof Error ? error : new Error(String(error))
|
|
390
|
+
|
|
391
|
+
// Only retry on rate limit errors
|
|
392
|
+
if (!isRateLimitError(lastError)) {
|
|
393
|
+
throw lastError
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Don't retry on the last attempt
|
|
397
|
+
if (attempt >= maxRetries) {
|
|
398
|
+
break
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
throw lastError || new Error('Request failed after all retries')
|
|
404
|
+
}
|
|
405
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"strict": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"rewriteRelativeImportExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"erasableSyntaxOnly": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
}
|
|
18
|
+
}
|