@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/evm/index.ts
ADDED
|
@@ -0,0 +1,1262 @@
|
|
|
1
|
+
import util from 'util'
|
|
2
|
+
|
|
3
|
+
import { parseAbi } from 'abitype'
|
|
4
|
+
import {
|
|
5
|
+
type BytesLike,
|
|
6
|
+
type JsonRpcApiProvider,
|
|
7
|
+
type Log,
|
|
8
|
+
type Provider,
|
|
9
|
+
type Signer,
|
|
10
|
+
type TransactionReceipt,
|
|
11
|
+
AbstractSigner,
|
|
12
|
+
BaseWallet,
|
|
13
|
+
Contract,
|
|
14
|
+
JsonRpcProvider,
|
|
15
|
+
Result,
|
|
16
|
+
SigningKey,
|
|
17
|
+
WebSocketProvider,
|
|
18
|
+
ZeroAddress,
|
|
19
|
+
concat,
|
|
20
|
+
dataSlice,
|
|
21
|
+
encodeBase58,
|
|
22
|
+
getAddress,
|
|
23
|
+
getBytes,
|
|
24
|
+
hexlify,
|
|
25
|
+
isBytesLike,
|
|
26
|
+
isHexString,
|
|
27
|
+
toBigInt,
|
|
28
|
+
zeroPadValue,
|
|
29
|
+
} from 'ethers'
|
|
30
|
+
import type { TypedContract } from 'ethers-abitype'
|
|
31
|
+
import moize, { type Key } from 'moize'
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
type ChainTransaction,
|
|
35
|
+
type LogFilter,
|
|
36
|
+
type TokenPoolRemote,
|
|
37
|
+
Chain,
|
|
38
|
+
ChainFamily,
|
|
39
|
+
} from '../chain.ts'
|
|
40
|
+
import {
|
|
41
|
+
type EVMExtraArgsV1,
|
|
42
|
+
type EVMExtraArgsV2,
|
|
43
|
+
type ExtraArgs,
|
|
44
|
+
type SVMExtraArgsV1,
|
|
45
|
+
type SuiExtraArgsV1,
|
|
46
|
+
EVMExtraArgsV1Tag,
|
|
47
|
+
EVMExtraArgsV2Tag,
|
|
48
|
+
SVMExtraArgsV1Tag,
|
|
49
|
+
SuiExtraArgsV1Tag,
|
|
50
|
+
} from '../extra-args.ts'
|
|
51
|
+
import type { LeafHasher } from '../hasher/common.ts'
|
|
52
|
+
import { supportedChains } from '../supported-chains.ts'
|
|
53
|
+
import {
|
|
54
|
+
type AnyMessage,
|
|
55
|
+
type CCIPMessage,
|
|
56
|
+
type CCIPRequest,
|
|
57
|
+
type CommitReport,
|
|
58
|
+
type ExecutionReceipt,
|
|
59
|
+
type ExecutionReport,
|
|
60
|
+
type ExecutionState,
|
|
61
|
+
type Lane,
|
|
62
|
+
type Log_,
|
|
63
|
+
type NetworkInfo,
|
|
64
|
+
type OffchainTokenData,
|
|
65
|
+
CCIPVersion,
|
|
66
|
+
} from '../types.ts'
|
|
67
|
+
import {
|
|
68
|
+
blockRangeGenerator,
|
|
69
|
+
decodeAddress,
|
|
70
|
+
decodeOnRampAddress,
|
|
71
|
+
getAddressBytes,
|
|
72
|
+
getDataBytes,
|
|
73
|
+
getSomeBlockNumberBefore,
|
|
74
|
+
networkInfo,
|
|
75
|
+
parseTypeAndVersion,
|
|
76
|
+
} from '../utils.ts'
|
|
77
|
+
import type Token_ABI from './abi/BurnMintERC677Token.ts'
|
|
78
|
+
import type FeeQuoter_ABI from './abi/FeeQuoter_1_6.ts'
|
|
79
|
+
import type TokenPool_1_5_ABI from './abi/LockReleaseTokenPool_1_5.ts'
|
|
80
|
+
import type TokenPool_ABI from './abi/LockReleaseTokenPool_1_6_1.ts'
|
|
81
|
+
import EVM2EVMOffRamp_1_2_ABI from './abi/OffRamp_1_2.ts'
|
|
82
|
+
import EVM2EVMOffRamp_1_5_ABI from './abi/OffRamp_1_5.ts'
|
|
83
|
+
import OffRamp_1_6_ABI from './abi/OffRamp_1_6.ts'
|
|
84
|
+
import EVM2EVMOnRamp_1_2_ABI from './abi/OnRamp_1_2.ts'
|
|
85
|
+
import EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
|
|
86
|
+
import OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
|
|
87
|
+
import type Router_ABI from './abi/Router.ts'
|
|
88
|
+
import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
|
|
89
|
+
import {
|
|
90
|
+
DEFAULT_APPROVE_GAS_LIMIT,
|
|
91
|
+
DEFAULT_GAS_LIMIT,
|
|
92
|
+
commitsFragments,
|
|
93
|
+
defaultAbiCoder,
|
|
94
|
+
getAllFragmentsMatchingEvents,
|
|
95
|
+
interfaces,
|
|
96
|
+
receiptsFragments,
|
|
97
|
+
requestsFragments,
|
|
98
|
+
} from './const.ts'
|
|
99
|
+
import { parseData } from './errors.ts'
|
|
100
|
+
import { getV12LeafHasher, getV16LeafHasher } from './hasher.ts'
|
|
101
|
+
import {
|
|
102
|
+
type CCIPMessage_V1_6_EVM,
|
|
103
|
+
type CleanAddressable,
|
|
104
|
+
parseSourceTokenData,
|
|
105
|
+
} from './messages.ts'
|
|
106
|
+
import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from './offchain.ts'
|
|
107
|
+
|
|
108
|
+
const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)'])
|
|
109
|
+
|
|
110
|
+
const EVMExtraArgsV1 = 'tuple(uint256 gasLimit)'
|
|
111
|
+
const EVMExtraArgsV2 = 'tuple(uint256 gasLimit, bool allowOutOfOrderExecution)'
|
|
112
|
+
const SVMExtraArgsV1 =
|
|
113
|
+
'tuple(uint32 computeUnits, uint64 accountIsWritableBitmap, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] accounts)'
|
|
114
|
+
const SuiExtraArgsV1 =
|
|
115
|
+
'tuple(uint256 gasLimit, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] receiverObjectIds)'
|
|
116
|
+
|
|
117
|
+
function resultToObject<T>(o: T): T {
|
|
118
|
+
return o instanceof Promise
|
|
119
|
+
? (o.then(resultToObject) as T)
|
|
120
|
+
: o instanceof Result
|
|
121
|
+
? (o.toObject() as T)
|
|
122
|
+
: o
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function resultsToMessage(result: Result): Record<string, unknown> {
|
|
126
|
+
if (result.message) result = result.message as Result
|
|
127
|
+
return {
|
|
128
|
+
...result.toObject(),
|
|
129
|
+
tokenAmounts: (result.tokenAmounts as Result[]).map((ta) => ta.toObject()),
|
|
130
|
+
...(result.sourceTokenData
|
|
131
|
+
? { sourceTokenData: (result.sourceTokenData as Result).toArray() }
|
|
132
|
+
: {}),
|
|
133
|
+
...(result.header ? { header: (result.header as Result).toObject() } : {}),
|
|
134
|
+
} as unknown as CCIPMessage
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
138
|
+
static readonly family = ChainFamily.EVM
|
|
139
|
+
static readonly decimals = 18
|
|
140
|
+
|
|
141
|
+
readonly network: NetworkInfo<typeof ChainFamily.EVM>
|
|
142
|
+
readonly provider: JsonRpcApiProvider
|
|
143
|
+
|
|
144
|
+
constructor(provider: JsonRpcApiProvider, network: NetworkInfo) {
|
|
145
|
+
if (network.family !== ChainFamily.EVM)
|
|
146
|
+
throw new Error(`Invalid network family for EVMChain: ${network.family}`)
|
|
147
|
+
super()
|
|
148
|
+
|
|
149
|
+
this.network = network
|
|
150
|
+
this.provider = provider
|
|
151
|
+
|
|
152
|
+
this.typeAndVersion = moize.default(this.typeAndVersion.bind(this))
|
|
153
|
+
this.getBlockTimestamp = moize.default(this.getBlockTimestamp.bind(this), {
|
|
154
|
+
maxSize: 100,
|
|
155
|
+
updateCacheForKey: (key: Key[]) => typeof key[key.length - 1] !== 'number',
|
|
156
|
+
})
|
|
157
|
+
this.getTransaction = moize.default(this.getTransaction.bind(this), {
|
|
158
|
+
maxSize: 100,
|
|
159
|
+
transformArgs: (args: Key[]) =>
|
|
160
|
+
typeof args[0] !== 'string'
|
|
161
|
+
? [(args[0] as unknown as TransactionReceipt).hash]
|
|
162
|
+
: (args as unknown as string[]),
|
|
163
|
+
})
|
|
164
|
+
this.getTokenForTokenPool = moize.default(this.getTokenForTokenPool.bind(this))
|
|
165
|
+
this.getNativeTokenForRouter = moize.default(this.getNativeTokenForRouter.bind(this), {
|
|
166
|
+
maxArgs: 1,
|
|
167
|
+
isPromise: true,
|
|
168
|
+
})
|
|
169
|
+
this.getTokenInfo = moize.default(this.getTokenInfo.bind(this))
|
|
170
|
+
this.getWallet = moize.default(this.getWallet.bind(this), { maxSize: 1, maxArgs: 0 })
|
|
171
|
+
this.getTokenAdminRegistryFor = moize.default(this.getTokenAdminRegistryFor.bind(this), {
|
|
172
|
+
isPromise: true,
|
|
173
|
+
maxArgs: 1,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// overwrite EVMChain.getWallet to implement custom wallet loading
|
|
178
|
+
// some signers don't like to be `.connect`ed, so pass provider as first param
|
|
179
|
+
static getWallet(_provider: Provider, _opts: { wallet?: unknown }): Promise<Signer> {
|
|
180
|
+
throw new Error('static EVM wallet loading not available')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// cached wallet/signer getter
|
|
184
|
+
async getWallet(opts: { wallet?: unknown } = {}): Promise<Signer> {
|
|
185
|
+
if (
|
|
186
|
+
typeof opts.wallet === 'number' ||
|
|
187
|
+
(typeof opts.wallet === 'string' && opts.wallet.match(/^(\d+|0x[a-fA-F0-9]{40})$/))
|
|
188
|
+
) {
|
|
189
|
+
// if given a number, numeric string or address, use ethers `provider.getSigner` (e.g. geth or MM)
|
|
190
|
+
return this.provider.getSigner(
|
|
191
|
+
typeof opts.wallet === 'string' && opts.wallet.match(/^0x[a-fA-F0-9]{40}$/)
|
|
192
|
+
? opts.wallet
|
|
193
|
+
: Number(opts.wallet),
|
|
194
|
+
)
|
|
195
|
+
} else if (typeof opts.wallet === 'string') {
|
|
196
|
+
// support receiving private key directly (not recommended)
|
|
197
|
+
try {
|
|
198
|
+
return Promise.resolve(
|
|
199
|
+
new BaseWallet(
|
|
200
|
+
new SigningKey((opts.wallet.startsWith('0x') ? '' : '0x') + opts.wallet),
|
|
201
|
+
this.provider,
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
} catch (_) {
|
|
205
|
+
// pass
|
|
206
|
+
}
|
|
207
|
+
} else if (opts.wallet instanceof AbstractSigner) {
|
|
208
|
+
// if given a signer, return/cache it
|
|
209
|
+
return opts.wallet
|
|
210
|
+
}
|
|
211
|
+
return (this.constructor as typeof EVMChain).getWallet(this.provider, opts)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Expose ethers provider's `listAccounts`, if provider supports it
|
|
216
|
+
*/
|
|
217
|
+
async listAccounts(): Promise<string[]> {
|
|
218
|
+
return (await this.provider.listAccounts()).map(({ address }) => address)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async getWalletAddress(opts?: { wallet?: unknown }): Promise<string> {
|
|
222
|
+
return (await this.getWallet(opts)).getAddress()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static async _getProvider(url: string): Promise<JsonRpcApiProvider> {
|
|
226
|
+
let provider: JsonRpcApiProvider
|
|
227
|
+
let providerReady: Promise<JsonRpcApiProvider>
|
|
228
|
+
if (url.startsWith('ws')) {
|
|
229
|
+
const provider_ = new WebSocketProvider(url)
|
|
230
|
+
providerReady = new Promise((resolve, reject) => {
|
|
231
|
+
provider_.websocket.onerror = reject
|
|
232
|
+
provider_
|
|
233
|
+
._waitUntilReady()
|
|
234
|
+
.then(() => resolve(provider_))
|
|
235
|
+
.catch(reject)
|
|
236
|
+
})
|
|
237
|
+
provider = provider_
|
|
238
|
+
} else if (url.startsWith('http')) {
|
|
239
|
+
provider = new JsonRpcProvider(url)
|
|
240
|
+
providerReady = Promise.resolve(provider)
|
|
241
|
+
} else {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Unknown JSON RPC protocol in endpoint (should be wss?:// or https?://): ${url}`,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
return providerReady
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static async fromProvider(provider: JsonRpcApiProvider): Promise<EVMChain> {
|
|
250
|
+
try {
|
|
251
|
+
return new EVMChain(provider, networkInfo(Number((await provider.getNetwork()).chainId)))
|
|
252
|
+
} catch (err) {
|
|
253
|
+
provider.destroy()
|
|
254
|
+
throw err
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static async fromUrl(url: string): Promise<EVMChain> {
|
|
259
|
+
return this.fromProvider(await this._getProvider(url))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
263
|
+
async destroy(): Promise<void> {
|
|
264
|
+
this.provider.destroy()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getBlockTimestamp(block: number | 'finalized'): Promise<number> {
|
|
268
|
+
const res = await this.provider.getBlock(block)
|
|
269
|
+
if (!res) throw new Error(`Block not found: ${block}`)
|
|
270
|
+
return res.timestamp
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async getTransaction(hash: string | TransactionReceipt): Promise<ChainTransaction> {
|
|
274
|
+
const tx = typeof hash === 'string' ? await this.provider.getTransactionReceipt(hash) : hash
|
|
275
|
+
if (!tx) throw new Error(`Transaction not found: ${hash as string}`)
|
|
276
|
+
const timestamp = await this.getBlockTimestamp(tx.blockNumber)
|
|
277
|
+
const chainTx = {
|
|
278
|
+
...tx,
|
|
279
|
+
chain: this,
|
|
280
|
+
timestamp,
|
|
281
|
+
logs: [] as Log_[],
|
|
282
|
+
}
|
|
283
|
+
const logs: Log_[] = tx.logs.map((l) => Object.assign(l, { tx: chainTx }))
|
|
284
|
+
chainTx.logs = logs
|
|
285
|
+
return chainTx
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async *getLogs(filter: LogFilter): AsyncIterableIterator<Log> {
|
|
289
|
+
const endBlock = filter.endBlock ?? (await this.provider.getBlockNumber())
|
|
290
|
+
if (
|
|
291
|
+
filter.topics?.length &&
|
|
292
|
+
filter.topics.every((t: string | string[]): t is string => typeof t === 'string')
|
|
293
|
+
) {
|
|
294
|
+
const topics = new Set(
|
|
295
|
+
filter.topics
|
|
296
|
+
.filter(isHexString)
|
|
297
|
+
.concat(Object.keys(getAllFragmentsMatchingEvents(filter.topics)) as `0x${string}`[])
|
|
298
|
+
.flat(),
|
|
299
|
+
)
|
|
300
|
+
if (!topics.size) {
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
302
|
+
throw new Error(`Could not find matching topics: ${filter.topics}`)
|
|
303
|
+
}
|
|
304
|
+
filter.topics = [Array.from(topics)]
|
|
305
|
+
}
|
|
306
|
+
if (!filter.startBlock && filter.startTime) {
|
|
307
|
+
filter.startBlock = await getSomeBlockNumberBefore(
|
|
308
|
+
this.getBlockTimestamp.bind(this),
|
|
309
|
+
endBlock,
|
|
310
|
+
filter.startTime,
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
for (const blockRange of blockRangeGenerator({ ...filter, endBlock })) {
|
|
314
|
+
console.debug('evm getLogs:', {
|
|
315
|
+
...blockRange,
|
|
316
|
+
...(filter.address ? { address: filter.address } : {}),
|
|
317
|
+
...(filter.topics?.length ? { topics: filter.topics } : {}),
|
|
318
|
+
})
|
|
319
|
+
const logs = await this.provider.getLogs({
|
|
320
|
+
...blockRange,
|
|
321
|
+
...(filter.address ? { address: filter.address } : {}),
|
|
322
|
+
...(filter.topics?.length ? { topics: filter.topics } : {}),
|
|
323
|
+
})
|
|
324
|
+
if (!filter.startBlock) logs.reverse()
|
|
325
|
+
yield* logs
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
static decodeMessage(log: {
|
|
330
|
+
topics?: readonly string[]
|
|
331
|
+
data: unknown
|
|
332
|
+
}): CCIPMessage | undefined {
|
|
333
|
+
if (!isBytesLike(log.data)) throw new Error(`invalid data=${util.inspect(log.data)}`)
|
|
334
|
+
let fragments
|
|
335
|
+
if (log.topics?.[0]) {
|
|
336
|
+
fragments = [requestsFragments[log.topics[0] as `0x${string}`]]
|
|
337
|
+
if (!fragments[0]) return
|
|
338
|
+
} else {
|
|
339
|
+
fragments = Object.values(requestsFragments)
|
|
340
|
+
}
|
|
341
|
+
let message
|
|
342
|
+
for (const fragment of fragments) {
|
|
343
|
+
try {
|
|
344
|
+
// we don't actually use Interface instance here, `decodeEventLog` is mostly static when given a fragment
|
|
345
|
+
const result = interfaces.OnRamp_v1_6.decodeEventLog(fragment, log.data, log.topics)
|
|
346
|
+
message = resultsToMessage(result)
|
|
347
|
+
} catch (_) {
|
|
348
|
+
// try next fragment
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (!message) return
|
|
352
|
+
if (!isHexString(message.sender, 20)) throw new Error('could not decode CCIPMessage')
|
|
353
|
+
|
|
354
|
+
if (!message.header) {
|
|
355
|
+
// CCIPMessage_V1_2_EVM
|
|
356
|
+
message.header = {
|
|
357
|
+
messageId: message.messageId as string,
|
|
358
|
+
sequenceNumber: message.sequenceNumber as bigint,
|
|
359
|
+
nonce: message.nonce as bigint,
|
|
360
|
+
sourceChainSelector: message.sourceChainSelector as bigint,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const sourceFamily = networkInfo(
|
|
365
|
+
(message.header as { sourceChainSelector: bigint }).sourceChainSelector,
|
|
366
|
+
).family
|
|
367
|
+
let destFamily: ChainFamily = ChainFamily.EVM
|
|
368
|
+
if ((message.header as { destChainSelector: bigint } | undefined)?.destChainSelector) {
|
|
369
|
+
destFamily = networkInfo(
|
|
370
|
+
(message.header as { destChainSelector: bigint }).destChainSelector,
|
|
371
|
+
).family
|
|
372
|
+
}
|
|
373
|
+
// conversions to make any message version be compatible with latest v1.6
|
|
374
|
+
message.tokenAmounts = (message.tokenAmounts as Record<string, string | bigint | number>[]).map(
|
|
375
|
+
(tokenAmount, i) => {
|
|
376
|
+
if ('sourceTokenData' in message) {
|
|
377
|
+
// CCIPMessage_V1_2_EVM
|
|
378
|
+
try {
|
|
379
|
+
tokenAmount = {
|
|
380
|
+
...parseSourceTokenData(
|
|
381
|
+
(message as { sourceTokenData: string[] }).sourceTokenData[i],
|
|
382
|
+
),
|
|
383
|
+
...tokenAmount,
|
|
384
|
+
}
|
|
385
|
+
} catch (_) {
|
|
386
|
+
console.debug(
|
|
387
|
+
'legacy sourceTokenData:',
|
|
388
|
+
i,
|
|
389
|
+
(message as { sourceTokenData: string[] }).sourceTokenData[i],
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
|
|
394
|
+
// CCIPMessage_V1_6_EVM
|
|
395
|
+
tokenAmount.destGasAmount = toBigInt(getDataBytes(tokenAmount.destExecData))
|
|
396
|
+
}
|
|
397
|
+
// Can be undefined if the message is from before v1.5 and failed to parse sourceTokenData
|
|
398
|
+
if (tokenAmount.sourcePoolAddress) {
|
|
399
|
+
tokenAmount.sourcePoolAddress = decodeAddress(
|
|
400
|
+
tokenAmount.sourcePoolAddress as string,
|
|
401
|
+
sourceFamily,
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
if (tokenAmount.destTokenAddress) {
|
|
405
|
+
tokenAmount.destTokenAddress = decodeAddress(
|
|
406
|
+
tokenAmount.destTokenAddress as string,
|
|
407
|
+
destFamily,
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
return tokenAmount
|
|
411
|
+
},
|
|
412
|
+
)
|
|
413
|
+
message.sender = decodeAddress(message.sender, sourceFamily)
|
|
414
|
+
message.feeToken = decodeAddress(message.feeToken as string, sourceFamily)
|
|
415
|
+
message.receiver = decodeAddress(message.receiver as string, destFamily)
|
|
416
|
+
if (message.extraArgs) {
|
|
417
|
+
// v1.6+
|
|
418
|
+
const parsed = this.decodeExtraArgs(message.extraArgs as string)
|
|
419
|
+
if (!parsed) throw new Error(`Unknown extraArgs: ${message.extraArgs as string}`)
|
|
420
|
+
const { _tag, ...rest } = parsed
|
|
421
|
+
// merge parsed extraArgs to any family in message root object
|
|
422
|
+
Object.assign(message, rest)
|
|
423
|
+
} else if (message.nonce === 0n) {
|
|
424
|
+
// v1.2..v1.5 targets EVM only; extraArgs is not explicit, gasLimit is already in
|
|
425
|
+
// message body, allowOutOfOrderExecution (in v1.5) was present only as nonce=0
|
|
426
|
+
message.allowOutOfOrderExecution = true
|
|
427
|
+
}
|
|
428
|
+
return message as CCIPMessage
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
static decodeCommits(
|
|
432
|
+
log: { topics?: readonly string[]; data: unknown },
|
|
433
|
+
lane?: Omit<Lane, 'destChainSelector'>,
|
|
434
|
+
): CommitReport[] | undefined {
|
|
435
|
+
if (!isBytesLike(log.data)) throw new Error(`invalid data=${util.inspect(log.data)}`)
|
|
436
|
+
let fragments
|
|
437
|
+
if (log.topics?.[0]) {
|
|
438
|
+
const fragment = commitsFragments[log.topics[0] as `0x${string}`]
|
|
439
|
+
if (!fragment) return
|
|
440
|
+
const isCcipV15 = fragment.name === 'ReportAccepted'
|
|
441
|
+
// CCIP<=1.5 doesn't have lane info in event, so we need lane to be provided (e.g. from CommitStore's configs)
|
|
442
|
+
if (isCcipV15 && !lane) throw new Error('decoding commits from CCIP<=v1.5 requires lane')
|
|
443
|
+
fragments = [fragment]
|
|
444
|
+
} else fragments = Object.values(commitsFragments)
|
|
445
|
+
for (const fragment of fragments) {
|
|
446
|
+
let result
|
|
447
|
+
try {
|
|
448
|
+
result = interfaces.OffRamp_v1_6.decodeEventLog(fragment, log.data, log.topics)
|
|
449
|
+
} catch (_) {
|
|
450
|
+
continue
|
|
451
|
+
}
|
|
452
|
+
if (result.length === 1) result = result[0] as Result
|
|
453
|
+
const isCcipV15 = fragment.name === 'ReportAccepted'
|
|
454
|
+
if (isCcipV15) {
|
|
455
|
+
return [
|
|
456
|
+
{
|
|
457
|
+
merkleRoot: result.merkleRoot as string,
|
|
458
|
+
minSeqNr: (result.interval as Result).min as bigint,
|
|
459
|
+
maxSeqNr: (result.interval as Result).max as bigint,
|
|
460
|
+
sourceChainSelector: lane!.sourceChainSelector,
|
|
461
|
+
onRampAddress: lane!.onRamp,
|
|
462
|
+
},
|
|
463
|
+
]
|
|
464
|
+
} else {
|
|
465
|
+
const reports: CommitReport[] = []
|
|
466
|
+
for (const c of [...(result[0] as Result[]), ...(result[1] as Result[])]) {
|
|
467
|
+
// if ccip>=v1.6 and lane is provided, use it to filter reports; otherwise, include all
|
|
468
|
+
if (lane && c.sourceChainSelector !== lane.sourceChainSelector) continue
|
|
469
|
+
const onRampAddress = decodeOnRampAddress(
|
|
470
|
+
c.onRampAddress as string,
|
|
471
|
+
networkInfo(c.sourceChainSelector as bigint).family,
|
|
472
|
+
)
|
|
473
|
+
if (lane && onRampAddress !== lane.onRamp) continue
|
|
474
|
+
reports.push({ ...c.toObject(), onRampAddress } as CommitReport)
|
|
475
|
+
}
|
|
476
|
+
if (reports.length) return reports
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
static decodeReceipt(log: {
|
|
482
|
+
topics?: readonly string[]
|
|
483
|
+
data: unknown
|
|
484
|
+
}): ExecutionReceipt | undefined {
|
|
485
|
+
if (!isBytesLike(log.data)) throw new Error(`invalid data=${util.inspect(log.data)}`)
|
|
486
|
+
let fragments
|
|
487
|
+
if (log.topics?.[0]) {
|
|
488
|
+
fragments = [receiptsFragments[log.topics[0] as `0x${string}`]]
|
|
489
|
+
if (!fragments[0]) return
|
|
490
|
+
} else fragments = Object.values(receiptsFragments)
|
|
491
|
+
for (const fragment of fragments) {
|
|
492
|
+
try {
|
|
493
|
+
const result = interfaces.OffRamp_v1_6.decodeEventLog(fragment, log.data, log.topics)
|
|
494
|
+
return {
|
|
495
|
+
...result.toObject(),
|
|
496
|
+
// ...(fragment.inputs.filter((p) => p.indexed).map((p, i) => [p.name, log.topics[i+1]] as const)).
|
|
497
|
+
state: Number(result.state as bigint) as ExecutionState,
|
|
498
|
+
} as ExecutionReceipt
|
|
499
|
+
} catch (_) {
|
|
500
|
+
// continue
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static decodeExtraArgs(
|
|
506
|
+
extraArgs: BytesLike,
|
|
507
|
+
):
|
|
508
|
+
| (EVMExtraArgsV1 & { _tag: 'EVMExtraArgsV1' })
|
|
509
|
+
| (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
|
|
510
|
+
| (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
|
|
511
|
+
| (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' })
|
|
512
|
+
| undefined {
|
|
513
|
+
const data = getDataBytes(extraArgs),
|
|
514
|
+
tag = dataSlice(data, 0, 4)
|
|
515
|
+
switch (tag) {
|
|
516
|
+
case EVMExtraArgsV1Tag: {
|
|
517
|
+
const args = defaultAbiCoder.decode([EVMExtraArgsV1], dataSlice(data, 4))
|
|
518
|
+
return { ...((args[0] as Result).toObject() as EVMExtraArgsV1), _tag: 'EVMExtraArgsV1' }
|
|
519
|
+
}
|
|
520
|
+
case EVMExtraArgsV2Tag: {
|
|
521
|
+
const args = defaultAbiCoder.decode([EVMExtraArgsV2], dataSlice(data, 4))
|
|
522
|
+
return { ...((args[0] as Result).toObject() as EVMExtraArgsV2), _tag: 'EVMExtraArgsV2' }
|
|
523
|
+
}
|
|
524
|
+
case SVMExtraArgsV1Tag: {
|
|
525
|
+
const args = defaultAbiCoder.decode([SVMExtraArgsV1], dataSlice(data, 4))
|
|
526
|
+
const parsed = (args[0] as Result).toObject() as SVMExtraArgsV1
|
|
527
|
+
parsed.tokenReceiver = encodeBase58(parsed.tokenReceiver)
|
|
528
|
+
parsed.accounts = parsed.accounts.map((a: string) => encodeBase58(a))
|
|
529
|
+
return { ...parsed, _tag: 'SVMExtraArgsV1' }
|
|
530
|
+
}
|
|
531
|
+
case SuiExtraArgsV1Tag: {
|
|
532
|
+
const args = defaultAbiCoder.decode([SuiExtraArgsV1], dataSlice(data, 4))
|
|
533
|
+
const parsed = (args[0] as Result).toObject() as SuiExtraArgsV1
|
|
534
|
+
return {
|
|
535
|
+
...parsed,
|
|
536
|
+
receiverObjectIds: Array.from<string>(parsed.receiverObjectIds),
|
|
537
|
+
_tag: 'SuiExtraArgsV1',
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
default:
|
|
541
|
+
return undefined
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
static encodeExtraArgs(args: ExtraArgs): string {
|
|
546
|
+
if (!args) return '0x'
|
|
547
|
+
if ('computeUnits' in args) {
|
|
548
|
+
return concat([
|
|
549
|
+
SVMExtraArgsV1Tag,
|
|
550
|
+
defaultAbiCoder.encode(
|
|
551
|
+
[SVMExtraArgsV1],
|
|
552
|
+
[
|
|
553
|
+
{
|
|
554
|
+
...args,
|
|
555
|
+
tokenReceiver: getAddressBytes(args.tokenReceiver),
|
|
556
|
+
accounts: args.accounts.map((a) => getAddressBytes(a)),
|
|
557
|
+
},
|
|
558
|
+
],
|
|
559
|
+
),
|
|
560
|
+
])
|
|
561
|
+
} else if ('receiverObjectIds' in args) {
|
|
562
|
+
return concat([
|
|
563
|
+
SuiExtraArgsV1Tag,
|
|
564
|
+
defaultAbiCoder.encode(
|
|
565
|
+
[SuiExtraArgsV1],
|
|
566
|
+
[
|
|
567
|
+
{
|
|
568
|
+
...args,
|
|
569
|
+
tokenReceiver: zeroPadValue(getAddressBytes(args.tokenReceiver), 32),
|
|
570
|
+
receiverObjectIds: args.receiverObjectIds.map((a) => getDataBytes(a)),
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
),
|
|
574
|
+
])
|
|
575
|
+
} else if ('allowOutOfOrderExecution' in args) {
|
|
576
|
+
if (args.gasLimit == null) args.gasLimit = DEFAULT_GAS_LIMIT
|
|
577
|
+
return concat([EVMExtraArgsV2Tag, defaultAbiCoder.encode([EVMExtraArgsV2], [args])])
|
|
578
|
+
} else if (args.gasLimit != null) {
|
|
579
|
+
return concat([EVMExtraArgsV1Tag, defaultAbiCoder.encode([EVMExtraArgsV1], [args])])
|
|
580
|
+
}
|
|
581
|
+
return '0x'
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
static getAddress(bytes: BytesLike): string {
|
|
585
|
+
bytes = getBytes(bytes)
|
|
586
|
+
if (bytes.length < 20) throw new Error(`Invalid address: ${hexlify(bytes)}`)
|
|
587
|
+
else if (bytes.length > 20) {
|
|
588
|
+
if (bytes.slice(0, bytes.length - 20).every((b) => b === 0)) {
|
|
589
|
+
bytes = bytes.slice(-20)
|
|
590
|
+
} else {
|
|
591
|
+
throw new Error(`Invalid address: ${hexlify(bytes)}`)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return getAddress(hexlify(bytes))
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async typeAndVersion(address: string) {
|
|
598
|
+
const contract = new Contract(
|
|
599
|
+
address,
|
|
600
|
+
VersionedContractABI,
|
|
601
|
+
this.provider,
|
|
602
|
+
) as unknown as TypedContract<typeof VersionedContractABI>
|
|
603
|
+
return parseTypeAndVersion(await contract.typeAndVersion())
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async getLaneForOnRamp(onRamp: string): Promise<Lane> {
|
|
607
|
+
const [, version] = await this.typeAndVersion(onRamp)
|
|
608
|
+
const onRampABI = version === CCIPVersion.V1_2 ? EVM2EVMOnRamp_1_2_ABI : EVM2EVMOnRamp_1_5_ABI
|
|
609
|
+
const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
|
|
610
|
+
typeof onRampABI
|
|
611
|
+
>
|
|
612
|
+
// TODO: memo this call
|
|
613
|
+
const staticConfig = await contract.getStaticConfig()
|
|
614
|
+
if (!staticConfig.destChainSelector)
|
|
615
|
+
throw new Error(
|
|
616
|
+
`No destChainSelector in OnRamp.staticConfig: ${JSON.stringify(staticConfig)}`,
|
|
617
|
+
)
|
|
618
|
+
return {
|
|
619
|
+
sourceChainSelector: this.network.chainSelector,
|
|
620
|
+
destChainSelector: staticConfig.destChainSelector,
|
|
621
|
+
version: version as CCIPVersion,
|
|
622
|
+
onRamp,
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
async getRouterForOnRamp(onRamp: string, destChainSelector: bigint): Promise<string> {
|
|
627
|
+
const [, version] = await this.typeAndVersion(onRamp)
|
|
628
|
+
let onRampABI
|
|
629
|
+
switch (version) {
|
|
630
|
+
case CCIPVersion.V1_2:
|
|
631
|
+
onRampABI = EVM2EVMOnRamp_1_2_ABI
|
|
632
|
+
// falls through
|
|
633
|
+
case CCIPVersion.V1_5: {
|
|
634
|
+
onRampABI ??= EVM2EVMOnRamp_1_5_ABI
|
|
635
|
+
const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
|
|
636
|
+
typeof onRampABI
|
|
637
|
+
>
|
|
638
|
+
const { router } = await contract.getDynamicConfig()
|
|
639
|
+
return router as string
|
|
640
|
+
}
|
|
641
|
+
case CCIPVersion.V1_6: {
|
|
642
|
+
onRampABI = OnRamp_1_6_ABI
|
|
643
|
+
const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
|
|
644
|
+
typeof onRampABI
|
|
645
|
+
>
|
|
646
|
+
const [, , router] = await contract.getDestChainConfig(destChainSelector)
|
|
647
|
+
return router as string
|
|
648
|
+
}
|
|
649
|
+
default:
|
|
650
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async getRouterForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
655
|
+
const [, version] = await this.typeAndVersion(offRamp)
|
|
656
|
+
let offRampABI, router
|
|
657
|
+
switch (version) {
|
|
658
|
+
case CCIPVersion.V1_2:
|
|
659
|
+
offRampABI = EVM2EVMOffRamp_1_2_ABI
|
|
660
|
+
// falls through
|
|
661
|
+
case CCIPVersion.V1_5: {
|
|
662
|
+
offRampABI ??= EVM2EVMOffRamp_1_5_ABI
|
|
663
|
+
const contract = new Contract(
|
|
664
|
+
offRamp,
|
|
665
|
+
offRampABI,
|
|
666
|
+
this.provider,
|
|
667
|
+
) as unknown as TypedContract<typeof offRampABI>
|
|
668
|
+
;({ router } = await contract.getDynamicConfig())
|
|
669
|
+
break
|
|
670
|
+
}
|
|
671
|
+
case CCIPVersion.V1_6: {
|
|
672
|
+
offRampABI = OffRamp_1_6_ABI
|
|
673
|
+
const contract = new Contract(
|
|
674
|
+
offRamp,
|
|
675
|
+
offRampABI,
|
|
676
|
+
this.provider,
|
|
677
|
+
) as unknown as TypedContract<typeof offRampABI>
|
|
678
|
+
;({ router } = await contract.getSourceChainConfig(sourceChainSelector))
|
|
679
|
+
break
|
|
680
|
+
}
|
|
681
|
+
default:
|
|
682
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
683
|
+
}
|
|
684
|
+
return router as string
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
async getNativeTokenForRouter(router: string): Promise<string> {
|
|
688
|
+
const contract = new Contract(
|
|
689
|
+
router,
|
|
690
|
+
interfaces.Router,
|
|
691
|
+
this.provider,
|
|
692
|
+
) as unknown as TypedContract<typeof Router_ABI>
|
|
693
|
+
return contract.getWrappedNative() as Promise<string>
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async getOffRampsForRouter(router: string, sourceChainSelector: bigint): Promise<string[]> {
|
|
697
|
+
const contract = new Contract(
|
|
698
|
+
router,
|
|
699
|
+
interfaces.Router,
|
|
700
|
+
this.provider,
|
|
701
|
+
) as unknown as TypedContract<typeof Router_ABI>
|
|
702
|
+
const offRamps = await contract.getOffRamps()
|
|
703
|
+
return offRamps
|
|
704
|
+
.filter((offRamp) => offRamp.sourceChainSelector === sourceChainSelector)
|
|
705
|
+
.map(({ offRamp }) => offRamp) as string[]
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async getOnRampForRouter(router: string, destChainSelector: bigint): Promise<string> {
|
|
709
|
+
const contract = new Contract(
|
|
710
|
+
router,
|
|
711
|
+
interfaces.Router,
|
|
712
|
+
this.provider,
|
|
713
|
+
) as unknown as TypedContract<typeof Router_ABI>
|
|
714
|
+
return contract.getOnRamp(destChainSelector) as Promise<string>
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
718
|
+
const [, version] = await this.typeAndVersion(offRamp)
|
|
719
|
+
let offRampABI
|
|
720
|
+
switch (version) {
|
|
721
|
+
case CCIPVersion.V1_2:
|
|
722
|
+
offRampABI = EVM2EVMOffRamp_1_2_ABI
|
|
723
|
+
// falls through
|
|
724
|
+
case CCIPVersion.V1_5: {
|
|
725
|
+
offRampABI ??= EVM2EVMOffRamp_1_5_ABI
|
|
726
|
+
const contract = new Contract(
|
|
727
|
+
offRamp,
|
|
728
|
+
offRampABI,
|
|
729
|
+
this.provider,
|
|
730
|
+
) as unknown as TypedContract<typeof offRampABI>
|
|
731
|
+
const { onRamp } = await contract.getStaticConfig()
|
|
732
|
+
return onRamp as string
|
|
733
|
+
}
|
|
734
|
+
case CCIPVersion.V1_6: {
|
|
735
|
+
offRampABI = OffRamp_1_6_ABI
|
|
736
|
+
const contract = new Contract(
|
|
737
|
+
offRamp,
|
|
738
|
+
offRampABI,
|
|
739
|
+
this.provider,
|
|
740
|
+
) as unknown as TypedContract<typeof offRampABI>
|
|
741
|
+
const { onRamp } = await contract.getSourceChainConfig(sourceChainSelector)
|
|
742
|
+
return decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)
|
|
743
|
+
}
|
|
744
|
+
default:
|
|
745
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async getCommitStoreForOffRamp(offRamp: string): Promise<string> {
|
|
750
|
+
const [, version] = await this.typeAndVersion(offRamp)
|
|
751
|
+
let offRampABI
|
|
752
|
+
switch (version) {
|
|
753
|
+
case CCIPVersion.V1_2:
|
|
754
|
+
offRampABI = EVM2EVMOffRamp_1_2_ABI
|
|
755
|
+
// falls through
|
|
756
|
+
case CCIPVersion.V1_5: {
|
|
757
|
+
offRampABI ??= EVM2EVMOffRamp_1_5_ABI
|
|
758
|
+
const contract = new Contract(
|
|
759
|
+
offRamp,
|
|
760
|
+
offRampABI,
|
|
761
|
+
this.provider,
|
|
762
|
+
) as unknown as TypedContract<typeof offRampABI>
|
|
763
|
+
const { commitStore } = await contract.getStaticConfig()
|
|
764
|
+
return commitStore as string
|
|
765
|
+
}
|
|
766
|
+
case CCIPVersion.V1_6: {
|
|
767
|
+
return offRamp
|
|
768
|
+
}
|
|
769
|
+
default:
|
|
770
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async getTokenForTokenPool(tokenPool: string): Promise<string> {
|
|
775
|
+
const contract = new Contract(
|
|
776
|
+
tokenPool,
|
|
777
|
+
interfaces.TokenPool_v1_6,
|
|
778
|
+
this.provider,
|
|
779
|
+
) as unknown as TypedContract<typeof TokenPool_ABI>
|
|
780
|
+
return contract.getToken() as Promise<string>
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async getTokenInfo(token: string): Promise<{ decimals: number; symbol: string; name: string }> {
|
|
784
|
+
const contract = new Contract(
|
|
785
|
+
token,
|
|
786
|
+
interfaces.Token,
|
|
787
|
+
this.provider,
|
|
788
|
+
) as unknown as TypedContract<typeof Token_ABI>
|
|
789
|
+
const [symbol, decimals, name] = await Promise.all([
|
|
790
|
+
contract.symbol(),
|
|
791
|
+
contract.decimals(),
|
|
792
|
+
contract.name(),
|
|
793
|
+
])
|
|
794
|
+
return { symbol, decimals: Number(decimals), name }
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
static getDestLeafHasher({
|
|
798
|
+
sourceChainSelector,
|
|
799
|
+
destChainSelector,
|
|
800
|
+
onRamp,
|
|
801
|
+
version,
|
|
802
|
+
}: Lane): LeafHasher {
|
|
803
|
+
switch (version) {
|
|
804
|
+
case CCIPVersion.V1_2:
|
|
805
|
+
case CCIPVersion.V1_5:
|
|
806
|
+
if (networkInfo(sourceChainSelector).family !== ChainFamily.EVM)
|
|
807
|
+
throw new Error(`Unsupported source chain: ${sourceChainSelector}`)
|
|
808
|
+
return getV12LeafHasher(sourceChainSelector, destChainSelector, onRamp) as LeafHasher
|
|
809
|
+
case CCIPVersion.V1_6:
|
|
810
|
+
return getV16LeafHasher(sourceChainSelector, destChainSelector, onRamp) as LeafHasher
|
|
811
|
+
default:
|
|
812
|
+
throw new Error(`Unsupported hasher version for EVM: ${version as string}`)
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async _getSomeOnRampFor(router: string): Promise<string> {
|
|
817
|
+
// when given a router, we take any onRamp we can find, as usually they all use same registry
|
|
818
|
+
const someOtherNetwork = this.network.isTestnet
|
|
819
|
+
? this.network.name === 'ethereum-testnet-sepolia'
|
|
820
|
+
? 'avalanche-testnet-fuji'
|
|
821
|
+
: 'ethereum-testnet-sepolia'
|
|
822
|
+
: this.network.name === 'ethereum-mainnet'
|
|
823
|
+
? 'avalanche-mainnet'
|
|
824
|
+
: 'ethereum-mainnet'
|
|
825
|
+
return this.getOnRampForRouter(router, networkInfo(someOtherNetwork).chainSelector)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
async getTokenAdminRegistryFor(address: string): Promise<string> {
|
|
829
|
+
let [type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
830
|
+
if (type === 'TokenAdminRegistry') {
|
|
831
|
+
return address
|
|
832
|
+
} else if (type === 'Router') {
|
|
833
|
+
address = await this._getSomeOnRampFor(address)
|
|
834
|
+
;[type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
835
|
+
} else if (!type.includes('Ramp')) {
|
|
836
|
+
throw new Error(`Not a Router, Ramp or TokenAdminRegistry: ${address} is "${typeAndVersion}"`)
|
|
837
|
+
}
|
|
838
|
+
const contract = new Contract(
|
|
839
|
+
address,
|
|
840
|
+
version < CCIPVersion.V1_6
|
|
841
|
+
? type.includes('OnRamp')
|
|
842
|
+
? interfaces.EVM2EVMOnRamp_v1_5
|
|
843
|
+
: interfaces.EVM2EVMOffRamp_v1_5
|
|
844
|
+
: type.includes('OnRamp')
|
|
845
|
+
? interfaces.OnRamp_v1_6
|
|
846
|
+
: interfaces.OffRamp_v1_6,
|
|
847
|
+
this.provider,
|
|
848
|
+
) as unknown as TypedContract<typeof OnRamp_1_6_ABI | typeof OffRamp_1_6_ABI>
|
|
849
|
+
const { tokenAdminRegistry } = await contract.getStaticConfig()
|
|
850
|
+
return tokenAdminRegistry as string
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
async getFeeQuoterFor(address: string): Promise<string> {
|
|
854
|
+
let [type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
855
|
+
if (type === 'FeeQuoter') {
|
|
856
|
+
return address
|
|
857
|
+
} else if (type === 'Router') {
|
|
858
|
+
address = await this._getSomeOnRampFor(address)
|
|
859
|
+
;[type, version, typeAndVersion] = await this.typeAndVersion(address)
|
|
860
|
+
} else if (!type.includes('Ramp')) {
|
|
861
|
+
throw new Error(`Not a Router, Ramp or FeeQuoter: ${address} is "${typeAndVersion}"`)
|
|
862
|
+
}
|
|
863
|
+
if (version < CCIPVersion.V1_6)
|
|
864
|
+
throw new Error(`Version < v1.6 doesn't have feeQuoter: got=${version}`)
|
|
865
|
+
|
|
866
|
+
const contract = new Contract(
|
|
867
|
+
address,
|
|
868
|
+
type.includes('OnRamp') ? interfaces.OnRamp_v1_6 : interfaces.OffRamp_v1_6,
|
|
869
|
+
this.provider,
|
|
870
|
+
) as unknown as TypedContract<typeof OnRamp_1_6_ABI | typeof OffRamp_1_6_ABI>
|
|
871
|
+
const { feeQuoter } = await contract.getDynamicConfig()
|
|
872
|
+
return feeQuoter as string
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
async getFee(router_: string, destChainSelector: bigint, message: AnyMessage): Promise<bigint> {
|
|
876
|
+
const router = new Contract(
|
|
877
|
+
router_,
|
|
878
|
+
interfaces.Router,
|
|
879
|
+
this.provider,
|
|
880
|
+
) as unknown as TypedContract<typeof Router_ABI>
|
|
881
|
+
return router.getFee(destChainSelector, {
|
|
882
|
+
receiver: zeroPadValue(getAddressBytes(message.receiver), 32),
|
|
883
|
+
data: hexlify(message.data),
|
|
884
|
+
tokenAmounts: message.tokenAmounts ?? [],
|
|
885
|
+
feeToken: message.feeToken ?? ZeroAddress,
|
|
886
|
+
extraArgs: hexlify((this.constructor as typeof EVMChain).encodeExtraArgs(message.extraArgs)),
|
|
887
|
+
})
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async sendMessage(
|
|
891
|
+
router_: string,
|
|
892
|
+
destChainSelector: bigint,
|
|
893
|
+
message: AnyMessage & { fee: bigint },
|
|
894
|
+
opts?: { wallet?: unknown; approveMax?: boolean },
|
|
895
|
+
): Promise<ChainTransaction> {
|
|
896
|
+
const feeToken = message.feeToken ?? ZeroAddress
|
|
897
|
+
const receiver = zeroPadValue(getAddressBytes(message.receiver), 32)
|
|
898
|
+
const data = hexlify(message.data)
|
|
899
|
+
const extraArgs = hexlify(
|
|
900
|
+
(this.constructor as typeof EVMChain).encodeExtraArgs(message.extraArgs),
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
// make sure to approve once per token, for the total amount (including fee, if needed)
|
|
904
|
+
const amountsToApprove = (message.tokenAmounts ?? []).reduce(
|
|
905
|
+
(acc, { token, amount }) => ({ ...acc, [token]: (acc[token] ?? 0n) + amount }),
|
|
906
|
+
{} as { [token: string]: bigint },
|
|
907
|
+
)
|
|
908
|
+
if (feeToken !== ZeroAddress)
|
|
909
|
+
amountsToApprove[feeToken] = (amountsToApprove[feeToken] ?? 0n) + message.fee
|
|
910
|
+
|
|
911
|
+
const wallet = await this.getWallet(opts) // moized wallet arg (if called previously)
|
|
912
|
+
|
|
913
|
+
// approve all tokens (including fee token) in parallel
|
|
914
|
+
let nonce = await this.provider.getTransactionCount(await this.getWalletAddress())
|
|
915
|
+
await Promise.all(
|
|
916
|
+
Object.entries(amountsToApprove).map(async ([token, amount]) => {
|
|
917
|
+
const contract = new Contract(token, interfaces.Token, wallet) as unknown as TypedContract<
|
|
918
|
+
typeof Token_ABI
|
|
919
|
+
>
|
|
920
|
+
const allowance = await contract.allowance(await wallet.getAddress(), router_)
|
|
921
|
+
if (allowance < amount) {
|
|
922
|
+
const amnt = opts?.approveMax ? 2n ** 256n - 1n : amount
|
|
923
|
+
// optimization: hardcode nonce and gasLimit to send all approvals in parallel without estimating
|
|
924
|
+
console.info('Approving', amnt, 'of', token, 'tokens for router', router_)
|
|
925
|
+
const tx = await contract.approve(router_, amnt, {
|
|
926
|
+
nonce: nonce++,
|
|
927
|
+
gasLimit: DEFAULT_APPROVE_GAS_LIMIT,
|
|
928
|
+
})
|
|
929
|
+
console.info('=>', tx.hash)
|
|
930
|
+
await tx.wait(1, 60_000)
|
|
931
|
+
}
|
|
932
|
+
}),
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
const router = new Contract(router_, interfaces.Router, wallet) as unknown as TypedContract<
|
|
936
|
+
typeof Router_ABI
|
|
937
|
+
>
|
|
938
|
+
const tx = await router.ccipSend(
|
|
939
|
+
destChainSelector,
|
|
940
|
+
{
|
|
941
|
+
receiver,
|
|
942
|
+
data,
|
|
943
|
+
tokenAmounts: message.tokenAmounts ?? [],
|
|
944
|
+
extraArgs,
|
|
945
|
+
feeToken,
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
nonce: nonce++,
|
|
949
|
+
// if native fee, include it in value; otherwise, it's transferedFrom feeToken
|
|
950
|
+
...(feeToken === ZeroAddress ? { value: message.fee } : {}),
|
|
951
|
+
},
|
|
952
|
+
)
|
|
953
|
+
const receipt = await tx.wait(1)
|
|
954
|
+
return this.getTransaction(receipt!)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
fetchOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
|
|
958
|
+
return fetchEVMOffchainTokenData(request)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
async executeReport(
|
|
962
|
+
offRamp: string,
|
|
963
|
+
execReport: ExecutionReport,
|
|
964
|
+
opts?: { wallet?: string; gasLimit?: number; tokensGasLimit?: number },
|
|
965
|
+
) {
|
|
966
|
+
const [_, version] = await this.typeAndVersion(offRamp)
|
|
967
|
+
const wallet = await this.getWallet(opts)
|
|
968
|
+
|
|
969
|
+
let manualExecTx
|
|
970
|
+
const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData)
|
|
971
|
+
|
|
972
|
+
switch (version) {
|
|
973
|
+
case CCIPVersion.V1_2: {
|
|
974
|
+
const contract = new Contract(
|
|
975
|
+
offRamp,
|
|
976
|
+
EVM2EVMOffRamp_1_2_ABI,
|
|
977
|
+
wallet,
|
|
978
|
+
) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
|
|
979
|
+
const gasOverride = BigInt(opts?.gasLimit ?? 0)
|
|
980
|
+
manualExecTx = await contract.manuallyExecute(
|
|
981
|
+
{
|
|
982
|
+
...execReport,
|
|
983
|
+
proofs: execReport.proofs.map((d) => hexlify(d)),
|
|
984
|
+
messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_2>],
|
|
985
|
+
offchainTokenData: [offchainTokenData],
|
|
986
|
+
},
|
|
987
|
+
[gasOverride],
|
|
988
|
+
)
|
|
989
|
+
break
|
|
990
|
+
}
|
|
991
|
+
case CCIPVersion.V1_5: {
|
|
992
|
+
const contract = new Contract(
|
|
993
|
+
offRamp,
|
|
994
|
+
EVM2EVMOffRamp_1_5_ABI,
|
|
995
|
+
wallet,
|
|
996
|
+
) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_5_ABI>
|
|
997
|
+
manualExecTx = await contract.manuallyExecute(
|
|
998
|
+
{
|
|
999
|
+
...execReport,
|
|
1000
|
+
proofs: execReport.proofs.map((d) => hexlify(d)),
|
|
1001
|
+
messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_5>],
|
|
1002
|
+
offchainTokenData: [offchainTokenData],
|
|
1003
|
+
},
|
|
1004
|
+
[
|
|
1005
|
+
{
|
|
1006
|
+
receiverExecutionGasLimit: BigInt(opts?.gasLimit ?? 0),
|
|
1007
|
+
tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
|
|
1008
|
+
BigInt(opts?.tokensGasLimit ?? opts?.gasLimit ?? 0),
|
|
1009
|
+
),
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
)
|
|
1013
|
+
break
|
|
1014
|
+
}
|
|
1015
|
+
case CCIPVersion.V1_6: {
|
|
1016
|
+
// normalize message
|
|
1017
|
+
const sender = zeroPadValue(getAddressBytes(execReport.message.sender), 32)
|
|
1018
|
+
const tokenAmounts = (execReport.message as CCIPMessage_V1_6_EVM).tokenAmounts.map(
|
|
1019
|
+
(ta) => ({
|
|
1020
|
+
...ta,
|
|
1021
|
+
sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
|
|
1022
|
+
extraData: hexlify(getDataBytes(ta.extraData)),
|
|
1023
|
+
}),
|
|
1024
|
+
)
|
|
1025
|
+
const message = {
|
|
1026
|
+
...(execReport.message as CCIPMessage_V1_6_EVM),
|
|
1027
|
+
sender,
|
|
1028
|
+
tokenAmounts,
|
|
1029
|
+
}
|
|
1030
|
+
const contract = new Contract(offRamp, OffRamp_1_6_ABI, wallet) as unknown as TypedContract<
|
|
1031
|
+
typeof OffRamp_1_6_ABI
|
|
1032
|
+
>
|
|
1033
|
+
manualExecTx = await contract.manuallyExecute(
|
|
1034
|
+
[
|
|
1035
|
+
{
|
|
1036
|
+
...execReport,
|
|
1037
|
+
proofs: execReport.proofs.map((p) => hexlify(p)),
|
|
1038
|
+
sourceChainSelector: execReport.message.header.sourceChainSelector,
|
|
1039
|
+
messages: [message],
|
|
1040
|
+
offchainTokenData: [offchainTokenData],
|
|
1041
|
+
},
|
|
1042
|
+
],
|
|
1043
|
+
[
|
|
1044
|
+
[
|
|
1045
|
+
{
|
|
1046
|
+
receiverExecutionGasLimit: BigInt(opts?.gasLimit ?? 0),
|
|
1047
|
+
tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
|
|
1048
|
+
BigInt(opts?.tokensGasLimit ?? opts?.gasLimit ?? 0),
|
|
1049
|
+
),
|
|
1050
|
+
},
|
|
1051
|
+
],
|
|
1052
|
+
],
|
|
1053
|
+
)
|
|
1054
|
+
break
|
|
1055
|
+
}
|
|
1056
|
+
default:
|
|
1057
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
1058
|
+
}
|
|
1059
|
+
const receipt = await this.provider.waitForTransaction(manualExecTx.hash, 1, 60e3)
|
|
1060
|
+
if (!receipt?.hash) throw new Error(`Could not confirm exec tx: ${manualExecTx.hash}`)
|
|
1061
|
+
if (!receipt.status) throw new Error(`Exec transaction reverted: ${manualExecTx.hash}`)
|
|
1062
|
+
return this.getTransaction(receipt)
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
static parse(data: unknown) {
|
|
1066
|
+
return parseData(data)
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Get the supported tokens for a given contract address
|
|
1071
|
+
*
|
|
1072
|
+
* @param address Router, OnRamp, OffRamp or TokenAdminRegistry contract
|
|
1073
|
+
* @returns An array of supported token addresses.
|
|
1074
|
+
*/
|
|
1075
|
+
async getSupportedTokens(registry: string, opts?: { page?: number }): Promise<string[]> {
|
|
1076
|
+
const contract = new Contract(
|
|
1077
|
+
registry,
|
|
1078
|
+
interfaces.TokenAdminRegistry,
|
|
1079
|
+
this.provider,
|
|
1080
|
+
) as unknown as TypedContract<typeof TokenAdminRegistry_1_5_ABI>
|
|
1081
|
+
|
|
1082
|
+
const limit = (opts?.page ?? 1000) || Number.MAX_SAFE_INTEGER
|
|
1083
|
+
const res = []
|
|
1084
|
+
let page
|
|
1085
|
+
do {
|
|
1086
|
+
page = await contract.getAllConfiguredTokens(BigInt(res.length), BigInt(limit))
|
|
1087
|
+
res.push(...page)
|
|
1088
|
+
} while (page.length === limit)
|
|
1089
|
+
return res as string[]
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
async getRegistryTokenConfig(
|
|
1093
|
+
registry: string,
|
|
1094
|
+
token: string,
|
|
1095
|
+
): Promise<{
|
|
1096
|
+
administrator: string
|
|
1097
|
+
pendingAdministrator?: string
|
|
1098
|
+
tokenPool?: string
|
|
1099
|
+
}> {
|
|
1100
|
+
const contract = new Contract(
|
|
1101
|
+
registry,
|
|
1102
|
+
interfaces.TokenAdminRegistry,
|
|
1103
|
+
this.provider,
|
|
1104
|
+
) as unknown as TypedContract<typeof TokenAdminRegistry_1_5_ABI>
|
|
1105
|
+
|
|
1106
|
+
const config = (await resultToObject(contract.getTokenConfig(token))) as CleanAddressable<
|
|
1107
|
+
Partial<Awaited<ReturnType<(typeof contract)['getTokenConfig']>>>
|
|
1108
|
+
>
|
|
1109
|
+
if (!config.administrator || config.administrator === ZeroAddress)
|
|
1110
|
+
throw new Error(`Token ${token} is not configured in registry ${registry}`)
|
|
1111
|
+
if (!config.pendingAdministrator || config.pendingAdministrator === ZeroAddress)
|
|
1112
|
+
delete config.pendingAdministrator
|
|
1113
|
+
if (!config.tokenPool || config.tokenPool === ZeroAddress) delete config.tokenPool
|
|
1114
|
+
return {
|
|
1115
|
+
...config,
|
|
1116
|
+
administrator: config.administrator,
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
async getTokenPoolConfigs(tokenPool: string): Promise<{
|
|
1121
|
+
token: string
|
|
1122
|
+
router: string
|
|
1123
|
+
typeAndVersion: string
|
|
1124
|
+
}> {
|
|
1125
|
+
const [_, , typeAndVersion] = await this.typeAndVersion(tokenPool)
|
|
1126
|
+
|
|
1127
|
+
const contract = new Contract(
|
|
1128
|
+
tokenPool,
|
|
1129
|
+
interfaces.TokenPool_v1_6,
|
|
1130
|
+
this.provider,
|
|
1131
|
+
) as unknown as TypedContract<typeof TokenPool_ABI>
|
|
1132
|
+
|
|
1133
|
+
const token = contract.getToken()
|
|
1134
|
+
const router = contract.getRouter()
|
|
1135
|
+
return Promise.all([token, router]).then(([token, router]) => {
|
|
1136
|
+
return {
|
|
1137
|
+
token: token as string,
|
|
1138
|
+
router: router as string,
|
|
1139
|
+
typeAndVersion,
|
|
1140
|
+
}
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
async getTokenPoolRemotes(
|
|
1145
|
+
tokenPool: string,
|
|
1146
|
+
remoteChainSelector?: bigint,
|
|
1147
|
+
): Promise<Record<string, TokenPoolRemote>> {
|
|
1148
|
+
const [_, version] = await this.typeAndVersion(tokenPool)
|
|
1149
|
+
|
|
1150
|
+
let supportedChains: Promise<NetworkInfo[]>
|
|
1151
|
+
if (remoteChainSelector) supportedChains = Promise.resolve([networkInfo(remoteChainSelector)])
|
|
1152
|
+
|
|
1153
|
+
let remotePools: Promise<string[][]>
|
|
1154
|
+
let contract
|
|
1155
|
+
if (version < '1.5.1') {
|
|
1156
|
+
const contract_ = new Contract(
|
|
1157
|
+
tokenPool,
|
|
1158
|
+
interfaces.TokenPool_v1_5,
|
|
1159
|
+
this.provider,
|
|
1160
|
+
) as unknown as TypedContract<typeof TokenPool_1_5_ABI>
|
|
1161
|
+
contract = contract_
|
|
1162
|
+
supportedChains ??= contract.getSupportedChains().then((chains) => chains.map(networkInfo))
|
|
1163
|
+
remotePools = supportedChains.then((chains) =>
|
|
1164
|
+
Promise.all(
|
|
1165
|
+
chains.map((chain) =>
|
|
1166
|
+
contract_
|
|
1167
|
+
.getRemotePool(chain.chainSelector)
|
|
1168
|
+
.then((remotePool) => [decodeAddress(remotePool, chain.family)]),
|
|
1169
|
+
),
|
|
1170
|
+
),
|
|
1171
|
+
)
|
|
1172
|
+
} else {
|
|
1173
|
+
const contract_ = new Contract(
|
|
1174
|
+
tokenPool,
|
|
1175
|
+
interfaces.TokenPool_v1_6,
|
|
1176
|
+
this.provider,
|
|
1177
|
+
) as unknown as TypedContract<typeof TokenPool_ABI>
|
|
1178
|
+
contract = contract_
|
|
1179
|
+
supportedChains ??= contract.getSupportedChains().then((chains) => chains.map(networkInfo))
|
|
1180
|
+
remotePools = supportedChains.then((chains) =>
|
|
1181
|
+
Promise.all(
|
|
1182
|
+
chains.map((chain) =>
|
|
1183
|
+
contract_
|
|
1184
|
+
.getRemotePools(chain.chainSelector)
|
|
1185
|
+
.then((pools) => pools.map((remotePool) => decodeAddress(remotePool, chain.family))),
|
|
1186
|
+
),
|
|
1187
|
+
),
|
|
1188
|
+
)
|
|
1189
|
+
}
|
|
1190
|
+
const remoteInfo = supportedChains.then((chains) =>
|
|
1191
|
+
Promise.all(
|
|
1192
|
+
chains.map((chain) =>
|
|
1193
|
+
Promise.all([
|
|
1194
|
+
contract.getRemoteToken(chain.chainSelector),
|
|
1195
|
+
resultToObject(contract.getCurrentInboundRateLimiterState(chain.chainSelector)),
|
|
1196
|
+
resultToObject(contract.getCurrentOutboundRateLimiterState(chain.chainSelector)),
|
|
1197
|
+
] as const),
|
|
1198
|
+
),
|
|
1199
|
+
),
|
|
1200
|
+
)
|
|
1201
|
+
return Promise.all([supportedChains, remotePools, remoteInfo]).then(
|
|
1202
|
+
([supportedChains, remotePools, remoteInfo]) =>
|
|
1203
|
+
Object.fromEntries(
|
|
1204
|
+
supportedChains.map(
|
|
1205
|
+
(chain, i) =>
|
|
1206
|
+
[
|
|
1207
|
+
chain.name,
|
|
1208
|
+
{
|
|
1209
|
+
remoteToken: decodeAddress(remoteInfo[i][0], chain.family),
|
|
1210
|
+
remotePools: remotePools[i].map((pool) => decodeAddress(pool, chain.family)),
|
|
1211
|
+
inboundRateLimiterState: remoteInfo[i][1].isEnabled ? remoteInfo[i][1] : null,
|
|
1212
|
+
outboundRateLimiterState: remoteInfo[i][2].isEnabled ? remoteInfo[i][2] : null,
|
|
1213
|
+
},
|
|
1214
|
+
] as const,
|
|
1215
|
+
),
|
|
1216
|
+
),
|
|
1217
|
+
)
|
|
1218
|
+
}
|
|
1219
|
+
async listFeeTokens(router: string) {
|
|
1220
|
+
const onRamp = await this._getSomeOnRampFor(router)
|
|
1221
|
+
const [_, version] = await this.typeAndVersion(onRamp)
|
|
1222
|
+
let tokens
|
|
1223
|
+
let onRampABI
|
|
1224
|
+
switch (version) {
|
|
1225
|
+
case CCIPVersion.V1_2:
|
|
1226
|
+
onRampABI = EVM2EVMOnRamp_1_2_ABI
|
|
1227
|
+
// falls through
|
|
1228
|
+
case CCIPVersion.V1_5: {
|
|
1229
|
+
onRampABI ??= EVM2EVMOnRamp_1_5_ABI
|
|
1230
|
+
const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
|
|
1231
|
+
typeof onRampABI
|
|
1232
|
+
>
|
|
1233
|
+
tokens = await Promise.all([
|
|
1234
|
+
this.getNativeTokenForRouter(router),
|
|
1235
|
+
contract.getStaticConfig().then(({ linkToken }) => linkToken),
|
|
1236
|
+
])
|
|
1237
|
+
break
|
|
1238
|
+
}
|
|
1239
|
+
case CCIPVersion.V1_6: {
|
|
1240
|
+
const feeQuoter = await this.getFeeQuoterFor(onRamp)
|
|
1241
|
+
const contract = new Contract(
|
|
1242
|
+
feeQuoter,
|
|
1243
|
+
interfaces.FeeQuoter,
|
|
1244
|
+
this.provider,
|
|
1245
|
+
) as unknown as TypedContract<typeof FeeQuoter_ABI>
|
|
1246
|
+
tokens = await contract.getFeeTokens()
|
|
1247
|
+
break
|
|
1248
|
+
}
|
|
1249
|
+
default:
|
|
1250
|
+
throw new Error(`Unsupported version: ${version}`)
|
|
1251
|
+
}
|
|
1252
|
+
return Object.fromEntries(
|
|
1253
|
+
await Promise.all(
|
|
1254
|
+
tokens.map(
|
|
1255
|
+
async (token) => [token as string, await this.getTokenInfo(token as string)] as const,
|
|
1256
|
+
),
|
|
1257
|
+
),
|
|
1258
|
+
)
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
supportedChains[ChainFamily.EVM] = EVMChain
|