@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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype'
|
|
2
|
+
import type { Addressable, Result } from 'ethers'
|
|
3
|
+
|
|
4
|
+
import type { EVMExtraArgsV2 } from '../extra-args.ts'
|
|
5
|
+
import type { CCIPVersion, MergeArrayElements } from '../types.ts'
|
|
6
|
+
import type EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
|
|
7
|
+
import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
|
|
8
|
+
import { defaultAbiCoder } from './const.ts'
|
|
9
|
+
|
|
10
|
+
// addresses often come as `string | Addressable`, this type cleans them up to just `string`
|
|
11
|
+
export type CleanAddressable<T> = T extends string | Addressable
|
|
12
|
+
? string
|
|
13
|
+
: T extends Record<string, unknown>
|
|
14
|
+
? { [K in keyof T]: CleanAddressable<T[K]> }
|
|
15
|
+
: T extends readonly unknown[]
|
|
16
|
+
? readonly CleanAddressable<T[number]>[]
|
|
17
|
+
: T
|
|
18
|
+
|
|
19
|
+
// v1.2-v1.5 Message ()
|
|
20
|
+
type EVM2AnyMessageRequested = CleanAddressable<
|
|
21
|
+
AbiParametersToPrimitiveTypes<
|
|
22
|
+
ExtractAbiEvent<typeof EVM2EVMOnRamp_1_5_ABI, 'CCIPSendRequested'>['inputs']
|
|
23
|
+
>[0]
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
// v1.6+ Message Base (all other dests share this intersection)
|
|
27
|
+
export type CCIPMessage_V1_6 = MergeArrayElements<
|
|
28
|
+
CleanAddressable<
|
|
29
|
+
AbiParametersToPrimitiveTypes<
|
|
30
|
+
ExtractAbiEvent<typeof OnRamp_1_6_ABI, 'CCIPMessageSent'>['inputs']
|
|
31
|
+
>[2]
|
|
32
|
+
>,
|
|
33
|
+
{ tokenAmounts: readonly SourceTokenData[] }
|
|
34
|
+
>
|
|
35
|
+
|
|
36
|
+
export type CCIPMessage_V1_5_EVM = MergeArrayElements<
|
|
37
|
+
EVM2AnyMessageRequested,
|
|
38
|
+
{
|
|
39
|
+
header: Omit<CCIPMessage_V1_6['header'], 'destChainSelector'>
|
|
40
|
+
tokenAmounts: readonly SourceTokenData[]
|
|
41
|
+
}
|
|
42
|
+
>
|
|
43
|
+
|
|
44
|
+
export type CCIPMessage_V1_2_EVM = EVM2AnyMessageRequested & {
|
|
45
|
+
header: Omit<CCIPMessage_V1_6['header'], 'destChainSelector'>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// v1.6 EVM specialization, extends CCIPMessage_V1_6, plus EVMExtraArgsV2 and tokenAmounts.*.destGasAmount
|
|
49
|
+
export type CCIPMessage_V1_6_EVM = CCIPMessage_V1_6 & EVMExtraArgsV2
|
|
50
|
+
|
|
51
|
+
export type CCIPMessage_EVM<V extends CCIPVersion = CCIPVersion> = V extends typeof CCIPVersion.V1_2
|
|
52
|
+
? CCIPMessage_V1_2_EVM
|
|
53
|
+
: V extends typeof CCIPVersion.V1_5
|
|
54
|
+
? CCIPMessage_V1_5_EVM
|
|
55
|
+
: CCIPMessage_V1_6_EVM
|
|
56
|
+
|
|
57
|
+
const SourceTokenData =
|
|
58
|
+
'tuple(bytes sourcePoolAddress, bytes destTokenAddress, bytes extraData, uint64 destGasAmount)'
|
|
59
|
+
export type SourceTokenData = {
|
|
60
|
+
sourcePoolAddress: string
|
|
61
|
+
destTokenAddress: string
|
|
62
|
+
extraData: string
|
|
63
|
+
destGasAmount: bigint
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* parse <=v1.5 `message.sourceTokenData`;
|
|
68
|
+
* v1.6+ already contains this in `message.tokenAmounts`
|
|
69
|
+
*/
|
|
70
|
+
export function parseSourceTokenData(data: string): SourceTokenData {
|
|
71
|
+
const decoded = defaultAbiCoder.decode([SourceTokenData], data)
|
|
72
|
+
return (decoded[0] as Result).toObject() as SourceTokenData
|
|
73
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { type Addressable, type Log, EventFragment } from 'ethers'
|
|
2
|
+
|
|
3
|
+
import { getLbtcAttestation, getUsdcAttestation } from '../offchain.ts'
|
|
4
|
+
import type { CCIPMessage, CCIPRequest, OffchainTokenData } from '../types.ts'
|
|
5
|
+
import { networkInfo } from '../utils.ts'
|
|
6
|
+
import { defaultAbiCoder, interfaces, requestsFragments } from './const.ts'
|
|
7
|
+
import { type SourceTokenData, parseSourceTokenData } from './messages.ts'
|
|
8
|
+
|
|
9
|
+
const BURNED_EVENT_1_5 = interfaces.TokenPool_v1_5.getEvent('Burned')!
|
|
10
|
+
const BURNED_EVENT_1_6 = interfaces.TokenPool_v1_6.getEvent('LockedOrBurned')!
|
|
11
|
+
const BURNED_EVENT_TOPIC_HASHES = new Set([BURNED_EVENT_1_5.topicHash, BURNED_EVENT_1_6.topicHash])
|
|
12
|
+
|
|
13
|
+
const USDC_EVENT = EventFragment.from('MessageSent(bytes message)')
|
|
14
|
+
const TRANSFER_EVENT = EventFragment.from('Transfer(address from, address to, uint256 value)')
|
|
15
|
+
|
|
16
|
+
export const LBTC_EVENT = EventFragment.from(
|
|
17
|
+
'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash, bytes payload)',
|
|
18
|
+
)
|
|
19
|
+
export const LBTC_EVENT_V2 = EventFragment.from(
|
|
20
|
+
'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash)',
|
|
21
|
+
)
|
|
22
|
+
const LBTC_EVENTS_HASHES = new Set([LBTC_EVENT.topicHash, LBTC_EVENT_V2.topicHash])
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fetch offchain token data for all transfers in request
|
|
26
|
+
*
|
|
27
|
+
* @param request - Request (or subset of) to fetch offchainTokenData for
|
|
28
|
+
* @returns Array of byte arrays, one per transfer in request
|
|
29
|
+
*/
|
|
30
|
+
export async function fetchEVMOffchainTokenData(
|
|
31
|
+
request: Pick<CCIPRequest, 'tx'> & {
|
|
32
|
+
message: CCIPMessage
|
|
33
|
+
log: Pick<CCIPRequest['log'], 'index'>
|
|
34
|
+
},
|
|
35
|
+
): Promise<OffchainTokenData[]> {
|
|
36
|
+
const { isTestnet } = networkInfo(request.message.header.sourceChainSelector)
|
|
37
|
+
// there's a chance there are other CCIPSendRequested in same tx,
|
|
38
|
+
// and they may contain USDC transfers as well, so we select
|
|
39
|
+
// any USDC logs after that and before our CCIPSendRequested
|
|
40
|
+
const prevCcipRequestIdx =
|
|
41
|
+
request.tx.logs.find(
|
|
42
|
+
({ topics, index }) => topics[0] in requestsFragments && index < request.log.index,
|
|
43
|
+
)?.index ?? -1
|
|
44
|
+
const usdcRequestLogs = request.tx.logs.filter(
|
|
45
|
+
({ index }) => prevCcipRequestIdx < index && index < request.log.index,
|
|
46
|
+
) as Log[]
|
|
47
|
+
|
|
48
|
+
const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(
|
|
49
|
+
() => undefined, // default tokenData
|
|
50
|
+
)
|
|
51
|
+
const usdcTokenData = await getUsdcTokenData(
|
|
52
|
+
request.message.tokenAmounts,
|
|
53
|
+
usdcRequestLogs,
|
|
54
|
+
isTestnet,
|
|
55
|
+
)
|
|
56
|
+
let lbtcTokenData: OffchainTokenData[] = []
|
|
57
|
+
try {
|
|
58
|
+
let tokenAmounts: readonly SourceTokenData[]
|
|
59
|
+
if ('sourceTokenData' in request.message) {
|
|
60
|
+
tokenAmounts = request.message.sourceTokenData.map(parseSourceTokenData)
|
|
61
|
+
} else {
|
|
62
|
+
tokenAmounts = request.message.tokenAmounts
|
|
63
|
+
}
|
|
64
|
+
//for lbtc we distinguish logs by hash in event, so we can pass all of them
|
|
65
|
+
lbtcTokenData = await getLbtcTokenData(tokenAmounts, request.tx.logs as Log[], isTestnet)
|
|
66
|
+
} catch (_) {
|
|
67
|
+
// pass
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < offchainTokenData.length; i++) {
|
|
71
|
+
if (usdcTokenData[i]) {
|
|
72
|
+
offchainTokenData[i] = usdcTokenData[i]
|
|
73
|
+
} else if (lbtcTokenData[i]) {
|
|
74
|
+
offchainTokenData[i] = lbtcTokenData[i]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return offchainTokenData
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function encodeEVMOffchainTokenData(data: OffchainTokenData): string {
|
|
82
|
+
if (data?._tag === 'usdc') {
|
|
83
|
+
return defaultAbiCoder.encode(['tuple(bytes message, bytes attestation)'], [data])
|
|
84
|
+
} else if (data?._tag === 'lbtc') {
|
|
85
|
+
return data.attestation as string
|
|
86
|
+
}
|
|
87
|
+
return '0x'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Try to fetch USDC attestations for transfers, return undefined in position if can't
|
|
92
|
+
*
|
|
93
|
+
* @param tokenAmounts - all tokenAmounts to try
|
|
94
|
+
* @param allLogsInRequest - all other logs in same tx as CCIPSendRequested
|
|
95
|
+
* @param isTestnet - use testnet CCTP API endpoint
|
|
96
|
+
* @returns array where each position is either the attestation for that transfer or undefined
|
|
97
|
+
**/
|
|
98
|
+
async function getUsdcTokenData(
|
|
99
|
+
tokenAmounts: CCIPMessage['tokenAmounts'],
|
|
100
|
+
allLogsInRequest: Pick<Log, 'topics' | 'address' | 'data'>[],
|
|
101
|
+
isTestnet: boolean,
|
|
102
|
+
): Promise<OffchainTokenData[]> {
|
|
103
|
+
const attestations: OffchainTokenData[] = []
|
|
104
|
+
|
|
105
|
+
const messageSentPerTokenAndPool = allLogsInRequest.reduce((acc, log, i, arr) => {
|
|
106
|
+
// for our MessageSent of interest (USDC-like), the token is the contract
|
|
107
|
+
// which emitted a (burn) Transfer immediately before this event, and the pool emitted a Burned
|
|
108
|
+
// event 2 events after
|
|
109
|
+
const transferLog = arr[i - 1]
|
|
110
|
+
const poolLog = arr[i + 2]
|
|
111
|
+
if (
|
|
112
|
+
log.topics[0] !== USDC_EVENT.topicHash ||
|
|
113
|
+
transferLog?.topics?.[0] !== TRANSFER_EVENT.topicHash ||
|
|
114
|
+
!BURNED_EVENT_TOPIC_HASHES.has(poolLog?.topics?.[0])
|
|
115
|
+
) {
|
|
116
|
+
return acc
|
|
117
|
+
}
|
|
118
|
+
const token = transferLog.address
|
|
119
|
+
const pool = poolLog.address
|
|
120
|
+
acc.set(token, [...(acc.get(token) ?? []), log])
|
|
121
|
+
acc.set(pool, [...(acc.get(pool) ?? []), log])
|
|
122
|
+
return acc
|
|
123
|
+
}, new Map<string | Addressable, (typeof allLogsInRequest)[number][]>())
|
|
124
|
+
|
|
125
|
+
for (const [i, tokenAmount] of tokenAmounts.entries()) {
|
|
126
|
+
const tokenOrPool = 'token' in tokenAmount ? tokenAmount.token : tokenAmount.sourcePoolAddress
|
|
127
|
+
|
|
128
|
+
// what if there are more USDC transfers of this same token after this one?
|
|
129
|
+
const tokenTransfersCountAfter = tokenAmounts.filter(
|
|
130
|
+
(ta, j) => ('token' in ta ? ta.token : ta.sourcePoolAddress) === tokenOrPool && j > i,
|
|
131
|
+
).length
|
|
132
|
+
|
|
133
|
+
let messageSentLog: (typeof allLogsInRequest)[number] | undefined
|
|
134
|
+
const messageSents = messageSentPerTokenAndPool.get(tokenOrPool)
|
|
135
|
+
if (messageSents) {
|
|
136
|
+
// look from the end (near our request), but skip MessageSents for further transfers
|
|
137
|
+
messageSentLog = messageSents[messageSents.length - 1 - tokenTransfersCountAfter]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let tokenData: OffchainTokenData
|
|
141
|
+
if (messageSentLog) {
|
|
142
|
+
let message
|
|
143
|
+
try {
|
|
144
|
+
message = defaultAbiCoder.decode(USDC_EVENT.inputs, messageSentLog.data)[0] as string
|
|
145
|
+
const attestation = await getUsdcAttestation(message, isTestnet)
|
|
146
|
+
tokenData = {
|
|
147
|
+
_tag: 'usdc',
|
|
148
|
+
message,
|
|
149
|
+
attestation,
|
|
150
|
+
}
|
|
151
|
+
// encoding of OffchainTokenData to be done as part of Chain.executeReceipt
|
|
152
|
+
} catch (err) {
|
|
153
|
+
// maybe not a USDC transfer, or not ready
|
|
154
|
+
console.warn(`❌ EVM CCTP: Failed to fetch attestation for message:`, message, err)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
attestations.push(tokenData)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return attestations
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Try to fetch LBTC attestations for transfers, return undefined in position if can't or not required
|
|
165
|
+
**/
|
|
166
|
+
async function getLbtcTokenData(
|
|
167
|
+
tokenAmounts: readonly SourceTokenData[],
|
|
168
|
+
allLogsInRequest: readonly Pick<Log, 'topics' | 'address' | 'data'>[],
|
|
169
|
+
isTestnet: boolean,
|
|
170
|
+
): Promise<OffchainTokenData[]> {
|
|
171
|
+
const lbtcDepositHashes = new Set(
|
|
172
|
+
allLogsInRequest
|
|
173
|
+
.filter(({ topics }) => LBTC_EVENTS_HASHES.has(topics[0]))
|
|
174
|
+
.map(({ topics }) => topics[3]),
|
|
175
|
+
)
|
|
176
|
+
return Promise.all(
|
|
177
|
+
tokenAmounts.map(async ({ extraData }) => {
|
|
178
|
+
// Attestation is required when SourceTokenData.extraData is 32 bytes long ('0x' + 64 hex chars)
|
|
179
|
+
// otherwise attestation is not required
|
|
180
|
+
if (lbtcDepositHashes.has(extraData)) {
|
|
181
|
+
try {
|
|
182
|
+
return { _tag: 'lbtc', extraData, ...(await getLbtcAttestation(extraData, isTestnet)) }
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.warn(`❌ EVM LBTC: Failed to fetch attestation for message:`, extraData, err)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
)
|
|
189
|
+
}
|
package/src/execution.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import moize from 'moize'
|
|
2
|
+
|
|
3
|
+
import type { Chain, ChainStatic } from './chain.ts'
|
|
4
|
+
import { Tree, getLeafHasher, proofFlagsToBits } from './hasher/index.ts'
|
|
5
|
+
import {
|
|
6
|
+
type CCIPExecution,
|
|
7
|
+
type CCIPMessage,
|
|
8
|
+
type CCIPVersion,
|
|
9
|
+
type CommitReport,
|
|
10
|
+
type ExecutionReport,
|
|
11
|
+
type Lane,
|
|
12
|
+
ExecutionState,
|
|
13
|
+
} from './types.ts'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Pure/sync function to calculate/generate OffRamp.executeManually report for messageIds
|
|
17
|
+
*
|
|
18
|
+
* @param messagesInBatch - Array containing all messages in batch, ordered
|
|
19
|
+
* @param lane - Arguments for leafeHasher (lane info)
|
|
20
|
+
* @param messageIds - list of messages (from batch) to prove for manual execution
|
|
21
|
+
* @param merkleRoot - Optional merkleRoot of the CommitReport, for validation
|
|
22
|
+
* @returns ManualExec report arguments
|
|
23
|
+
**/
|
|
24
|
+
export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
|
|
25
|
+
messagesInBatch: readonly CCIPMessage<V>[],
|
|
26
|
+
lane: Lane<V>,
|
|
27
|
+
messageId: string,
|
|
28
|
+
merkleRoot?: string,
|
|
29
|
+
): Omit<ExecutionReport, 'offchainTokenData' | 'message'> {
|
|
30
|
+
const hasher = getLeafHasher(lane)
|
|
31
|
+
|
|
32
|
+
const msgIdx = messagesInBatch.findIndex((message) => message.header.messageId === messageId)
|
|
33
|
+
if (msgIdx < 0) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Could not find ${messageId} in batch seqNums=[${Number(messagesInBatch[0].header.sequenceNumber)}..${Number(messagesInBatch[messagesInBatch.length - 1].header.sequenceNumber)}]`,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const leaves = messagesInBatch.map((message) => hasher(message))
|
|
40
|
+
|
|
41
|
+
// Create multi-merkle tree
|
|
42
|
+
const tree = new Tree(leaves)
|
|
43
|
+
if (merkleRoot && tree.root() !== merkleRoot) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Merkle root created from send events doesn't match ReportAccepted merkle root: expected=${merkleRoot}, got=${tree.root()}`,
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Generate proof from multi-merkle tree
|
|
50
|
+
const proof = tree.prove([msgIdx])
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
proofs: proof.hashes,
|
|
54
|
+
proofFlagBits: proofFlagsToBits(proof.sourceFlags),
|
|
55
|
+
merkleRoot: tree.root(),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const discoverOffRamp = moize.default(
|
|
60
|
+
async function discoverOffRamp_(source: Chain, dest: Chain, onRamp: string): Promise<string> {
|
|
61
|
+
const sourceRouter = await source.getRouterForOnRamp(onRamp, dest.network.chainSelector)
|
|
62
|
+
const sourceOffRamps = await source.getOffRampsForRouter(
|
|
63
|
+
sourceRouter,
|
|
64
|
+
dest.network.chainSelector,
|
|
65
|
+
)
|
|
66
|
+
for (const offRamp of sourceOffRamps) {
|
|
67
|
+
const destOnRamp = await source.getOnRampForOffRamp(offRamp, dest.network.chainSelector)
|
|
68
|
+
const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
|
|
69
|
+
const destOffRamps = await dest.getOffRampsForRouter(destRouter, source.network.chainSelector)
|
|
70
|
+
for (const offRamp of destOffRamps) {
|
|
71
|
+
const offRampsOnRamp = await dest.getOnRampForOffRamp(offRamp, source.network.chainSelector)
|
|
72
|
+
if (offRampsOnRamp === onRamp) {
|
|
73
|
+
console.debug(
|
|
74
|
+
'discoverOffRamp: found, from',
|
|
75
|
+
{ sourceRouter, sourceOffRamps, destOnRamp, destOffRamps, offRampsOnRamp },
|
|
76
|
+
'=',
|
|
77
|
+
offRamp,
|
|
78
|
+
)
|
|
79
|
+
return offRamp
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`No matching offRamp found for "${onRamp}" on "${dest.network.name}"`)
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
transformArgs: ([source, dest, onRamp]) => [
|
|
87
|
+
(source as Chain).network.chainSelector,
|
|
88
|
+
(dest as Chain).network.chainSelector,
|
|
89
|
+
onRamp as string,
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generic implementation for fetching ExecutionReceipts for given requests
|
|
96
|
+
* If more than one request is given, may yield them interleaved
|
|
97
|
+
* Completes as soon as there's no more work to be done
|
|
98
|
+
* 2 possible behaviors:
|
|
99
|
+
* - if `startBlock|startTime` is given, pages forward from that block up;
|
|
100
|
+
* completes when success (final) receipt is found for all requests (or reach latest block)
|
|
101
|
+
* - otherwise, pages backwards and returns only the most recent receipt per request;
|
|
102
|
+
* completes when receipts for all requests were seen
|
|
103
|
+
*
|
|
104
|
+
* @param dest - provider to page through
|
|
105
|
+
* @param requests - CCIP requests to search executions for
|
|
106
|
+
* @param hints.fromBlock - A block from where to start paging forward;
|
|
107
|
+
* otherwise, page backwards and completes on first (most recent) receipt
|
|
108
|
+
* @param hints.page - getLogs pagination range param
|
|
109
|
+
* @param hints.commit - Special param to help narrow down search on suppported chains (e.g. Solana)
|
|
110
|
+
**/
|
|
111
|
+
export async function* fetchExecutionReceipts(
|
|
112
|
+
dest: Chain,
|
|
113
|
+
offRamp: string,
|
|
114
|
+
messageIds: Set<string>,
|
|
115
|
+
hints?: { startBlock?: number; startTime?: number; page?: number; commit?: CommitReport },
|
|
116
|
+
): AsyncGenerator<CCIPExecution> {
|
|
117
|
+
const onlyLast = !hints?.startBlock && !hints?.startTime // backwards
|
|
118
|
+
for await (const log of dest.getLogs({
|
|
119
|
+
...hints,
|
|
120
|
+
address: offRamp,
|
|
121
|
+
topics: ['ExecutionStateChanged'],
|
|
122
|
+
})) {
|
|
123
|
+
const receipt = (dest.constructor as ChainStatic).decodeReceipt(log)
|
|
124
|
+
if (!receipt || !messageIds.has(receipt.messageId)) continue
|
|
125
|
+
if (onlyLast || receipt.state === ExecutionState.Success) messageIds.delete(receipt.messageId)
|
|
126
|
+
|
|
127
|
+
const timestamp = await dest.getBlockTimestamp(log.blockNumber)
|
|
128
|
+
yield { receipt, log, timestamp }
|
|
129
|
+
if (!messageIds.size) break
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type BytesLike, id } from 'ethers'
|
|
2
|
+
|
|
3
|
+
import { ChainFamily } from './chain.ts'
|
|
4
|
+
import { supportedChains } from './supported-chains.ts'
|
|
5
|
+
|
|
6
|
+
export const EVMExtraArgsV1Tag = id('CCIP EVMExtraArgsV1').substring(0, 10) as '0x97a657c9'
|
|
7
|
+
export const EVMExtraArgsV2Tag = id('CCIP EVMExtraArgsV2').substring(0, 10) as '0x181dcf10'
|
|
8
|
+
export const SVMExtraArgsV1Tag = id('CCIP SVMExtraArgsV1').substring(0, 10) as '0x1f3b3aba'
|
|
9
|
+
export const SuiExtraArgsV1Tag = id('CCIP SuiExtraArgsV1').substring(0, 10) as '0x21ea4ca9'
|
|
10
|
+
|
|
11
|
+
export type EVMExtraArgsV1 = {
|
|
12
|
+
gasLimit: bigint
|
|
13
|
+
}
|
|
14
|
+
// aka GenericExtraArgsV2
|
|
15
|
+
export type EVMExtraArgsV2 = EVMExtraArgsV1 & {
|
|
16
|
+
allowOutOfOrderExecution: boolean
|
|
17
|
+
}
|
|
18
|
+
export type SVMExtraArgsV1 = {
|
|
19
|
+
computeUnits: bigint
|
|
20
|
+
accountIsWritableBitmap: bigint
|
|
21
|
+
allowOutOfOrderExecution: boolean
|
|
22
|
+
tokenReceiver: string
|
|
23
|
+
accounts: string[]
|
|
24
|
+
}
|
|
25
|
+
export type SuiExtraArgsV1 = EVMExtraArgsV2 & {
|
|
26
|
+
tokenReceiver: string
|
|
27
|
+
receiverObjectIds: string[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ExtraArgs = EVMExtraArgsV1 | EVMExtraArgsV2 | SVMExtraArgsV1 | SuiExtraArgsV1
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Encodes extra arguments for CCIP messages.
|
|
34
|
+
* The args are *to* a dest network, but are encoded as a message *from* this source chain
|
|
35
|
+
* e.g. Solana uses Borsh to encode extraArgs in its produced requests, even those targetting EVM
|
|
36
|
+
**/
|
|
37
|
+
export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily.EVM): string {
|
|
38
|
+
const chain = supportedChains[from]
|
|
39
|
+
if (!chain) throw new Error(`Unsupported chain family: ${from}`)
|
|
40
|
+
return chain.encodeExtraArgs(args)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parses extra arguments from CCIP messages
|
|
45
|
+
* @param data - extra arguments bytearray data
|
|
46
|
+
* @returns extra arguments object if found
|
|
47
|
+
**/
|
|
48
|
+
export function decodeExtraArgs(
|
|
49
|
+
data: BytesLike,
|
|
50
|
+
from?: ChainFamily,
|
|
51
|
+
):
|
|
52
|
+
| (EVMExtraArgsV1 & { _tag: 'EVMExtraArgsV1' })
|
|
53
|
+
| (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
|
|
54
|
+
| (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
|
|
55
|
+
| (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' })
|
|
56
|
+
| undefined {
|
|
57
|
+
if (!data || data === '') return
|
|
58
|
+
let chains
|
|
59
|
+
if (from) {
|
|
60
|
+
const chain = supportedChains[from]
|
|
61
|
+
if (!chain) throw new Error(`Unsupported chain family: ${from}`)
|
|
62
|
+
chains = [chain]
|
|
63
|
+
} else {
|
|
64
|
+
chains = Object.values(supportedChains)
|
|
65
|
+
}
|
|
66
|
+
for (const chain of chains) {
|
|
67
|
+
const decoded = chain.decodeExtraArgs(data)
|
|
68
|
+
if (decoded) return decoded
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Could not parse extraArgs from "${from}"`)
|
|
71
|
+
}
|
package/src/gas.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BytesLike,
|
|
3
|
+
Contract,
|
|
4
|
+
FunctionFragment,
|
|
5
|
+
concat,
|
|
6
|
+
formatUnits,
|
|
7
|
+
getNumber,
|
|
8
|
+
hexlify,
|
|
9
|
+
randomBytes,
|
|
10
|
+
solidityPackedKeccak256,
|
|
11
|
+
toBeHex,
|
|
12
|
+
zeroPadValue,
|
|
13
|
+
} from 'ethers'
|
|
14
|
+
import type { TypedContract } from 'ethers-abitype'
|
|
15
|
+
|
|
16
|
+
import type { Chain } from './chain.ts'
|
|
17
|
+
import TokenABI from './evm/abi/BurnMintERC677Token.ts'
|
|
18
|
+
import RouterABI from './evm/abi/Router.ts'
|
|
19
|
+
import { defaultAbiCoder } from './evm/const.ts'
|
|
20
|
+
import type { EVMChain } from './evm/index.ts'
|
|
21
|
+
import { discoverOffRamp } from './execution.ts'
|
|
22
|
+
import type { Lane } from './types.ts'
|
|
23
|
+
|
|
24
|
+
const BALANCES_SLOT = 0
|
|
25
|
+
const ccipReceive = FunctionFragment.from({
|
|
26
|
+
type: 'function',
|
|
27
|
+
name: 'ccipReceive',
|
|
28
|
+
stateMutability: 'nonpayable',
|
|
29
|
+
inputs: RouterABI.find((v) => v.type === 'function' && v.name === 'routeMessage')!.inputs.slice(
|
|
30
|
+
0,
|
|
31
|
+
1,
|
|
32
|
+
),
|
|
33
|
+
outputs: [],
|
|
34
|
+
})
|
|
35
|
+
type Any2EVMMessage = Parameters<TypedContract<typeof RouterABI>['routeMessage']>[0]
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Estimate CCIP gasLimit needed to execute a request on a contract receiver
|
|
39
|
+
*
|
|
40
|
+
* @param dest - Provider for the destination chain
|
|
41
|
+
* @param request - CCIP request info
|
|
42
|
+
* @param request.lane - Lane info
|
|
43
|
+
* @param request.message - Message info
|
|
44
|
+
* @returns estimated gasLimit as bigint
|
|
45
|
+
**/
|
|
46
|
+
export async function estimateExecGasForRequest(
|
|
47
|
+
source: Chain,
|
|
48
|
+
dest: EVMChain,
|
|
49
|
+
request: {
|
|
50
|
+
lane: Lane
|
|
51
|
+
message: {
|
|
52
|
+
sender: string
|
|
53
|
+
receiver: string
|
|
54
|
+
data: BytesLike
|
|
55
|
+
tokenAmounts: readonly {
|
|
56
|
+
sourcePoolAddress: string
|
|
57
|
+
destTokenAddress: string
|
|
58
|
+
amount: bigint
|
|
59
|
+
}[]
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
) {
|
|
63
|
+
const offRamp = await discoverOffRamp(source, dest, request.lane.onRamp)
|
|
64
|
+
const destRouter = await dest.getRouterForOffRamp(offRamp, request.lane.sourceChainSelector)
|
|
65
|
+
|
|
66
|
+
const destTokenAmounts = await Promise.all(
|
|
67
|
+
request.message.tokenAmounts.map(async (ta) => {
|
|
68
|
+
if (!('destTokenAddress' in ta)) throw new Error('legacy <1.5 tokenPools not supported')
|
|
69
|
+
const [{ decimals: sourceDecimals }, { decimals: destDecimals }] = await Promise.all([
|
|
70
|
+
source
|
|
71
|
+
.getTokenForTokenPool(ta.sourcePoolAddress)
|
|
72
|
+
.then((token) => source.getTokenInfo(token)),
|
|
73
|
+
dest.getTokenInfo(ta.destTokenAddress),
|
|
74
|
+
])
|
|
75
|
+
const destAmount =
|
|
76
|
+
(ta.amount * 10n ** BigInt(destDecimals - sourceDecimals + 36)) / 10n ** 36n
|
|
77
|
+
if (destAmount === 0n)
|
|
78
|
+
throw new Error(
|
|
79
|
+
`not enough decimals=${destDecimals} for token=${ta.destTokenAddress} on dest=${dest.network.name} to express ${formatUnits(ta.amount, sourceDecimals)}`,
|
|
80
|
+
)
|
|
81
|
+
return { token: ta.destTokenAddress, amount: destAmount }
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const message: Any2EVMMessage = {
|
|
86
|
+
messageId: hexlify(randomBytes(32)),
|
|
87
|
+
sender: zeroPadValue(request.message.sender, 32),
|
|
88
|
+
data: hexlify(request.message.data),
|
|
89
|
+
sourceChainSelector: request.lane.sourceChainSelector,
|
|
90
|
+
destTokenAmounts,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// we need to override the state, increasing receiver's balance for each token, to simulate the
|
|
94
|
+
// state after tokens were transferred by the offRamp just before calling `ccipReceive`
|
|
95
|
+
const destAmounts: Record<string, bigint> = {}
|
|
96
|
+
const stateOverrides: Record<string, { stateDiff: Record<string, string> }> = {}
|
|
97
|
+
for (const { token, amount } of destTokenAmounts) {
|
|
98
|
+
if (!(token in destAmounts)) {
|
|
99
|
+
const tokenContract = new Contract(token, TokenABI, dest) as unknown as TypedContract<
|
|
100
|
+
typeof TokenABI
|
|
101
|
+
>
|
|
102
|
+
const currentBalance = await tokenContract.balanceOf(request.message.receiver)
|
|
103
|
+
destAmounts[token] = currentBalance
|
|
104
|
+
}
|
|
105
|
+
destAmounts[token] += amount
|
|
106
|
+
stateOverrides[token] = {
|
|
107
|
+
stateDiff: {
|
|
108
|
+
[solidityPackedKeccak256(
|
|
109
|
+
['uint256', 'uint256'],
|
|
110
|
+
[request.message.receiver, BALANCES_SLOT],
|
|
111
|
+
)]: toBeHex(destAmounts[token], 32),
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const calldata = concat([
|
|
117
|
+
ccipReceive.selector,
|
|
118
|
+
defaultAbiCoder.encode(ccipReceive.inputs, [message]),
|
|
119
|
+
])
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
getNumber(
|
|
123
|
+
(await dest.provider.send('eth_estimateGas', [
|
|
124
|
+
{
|
|
125
|
+
from: destRouter,
|
|
126
|
+
to: request.message.receiver,
|
|
127
|
+
data: calldata,
|
|
128
|
+
},
|
|
129
|
+
'latest',
|
|
130
|
+
...(Object.keys(stateOverrides).length ? [stateOverrides] : []),
|
|
131
|
+
])) as string,
|
|
132
|
+
) -
|
|
133
|
+
(21_000 - 700) // 21k is the base gas cost for a transaction, 700 is the gas cost of the call
|
|
134
|
+
)
|
|
135
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { concat, hexlify, keccak256, toBeHex } from 'ethers'
|
|
2
|
+
|
|
3
|
+
import type { CCIPMessage, CCIPVersion } from '../types.ts'
|
|
4
|
+
|
|
5
|
+
export type LeafHasher<V extends CCIPVersion = CCIPVersion> = (message: CCIPMessage<V>) => string
|
|
6
|
+
|
|
7
|
+
const INTERNAL_DOMAIN_SEPARATOR = toBeHex(1, 32)
|
|
8
|
+
export const LEAF_DOMAIN_SEPARATOR = '0x00'
|
|
9
|
+
export const ZERO_HASH = hexlify(new Uint8Array(32).fill(0xff))
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Computes the Keccak-256 hash of the concatenation of two hash values.
|
|
13
|
+
* @param a The first hash as a Hash type.
|
|
14
|
+
* @param b The second hash as a Hash type.
|
|
15
|
+
* @returns The Keccak-256 hash result as a Hash type.
|
|
16
|
+
*/
|
|
17
|
+
export function hashInternal(a: string, b: string): string {
|
|
18
|
+
if (a > b) {
|
|
19
|
+
;[a, b] = [b, a]
|
|
20
|
+
}
|
|
21
|
+
const combinedData = concat([INTERNAL_DOMAIN_SEPARATOR, a, b])
|
|
22
|
+
return keccak256(combinedData)
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { supportedChains } from '../supported-chains.ts'
|
|
2
|
+
import type { CCIPVersion, Lane } from '../types.ts'
|
|
3
|
+
import { networkInfo } from '../utils.ts'
|
|
4
|
+
import type { LeafHasher } from './common.ts'
|
|
5
|
+
|
|
6
|
+
// Factory function that returns the right encoder based on the version of the lane
|
|
7
|
+
export function getLeafHasher<V extends CCIPVersion = CCIPVersion>(lane: Lane<V>): LeafHasher<V> {
|
|
8
|
+
const destFamily = networkInfo(lane.destChainSelector).family
|
|
9
|
+
const chain = supportedChains[destFamily]
|
|
10
|
+
if (!chain) throw new Error(`Unsupported chain family: ${destFamily}`)
|
|
11
|
+
return chain.getDestLeafHasher(lane) as LeafHasher<V>
|
|
12
|
+
}
|