@chainlink/ccip-sdk 0.96.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/dist/api/index.d.ts +21 -8
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +42 -13
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +0 -2
- package/dist/api/types.d.ts.map +1 -1
- package/dist/aptos/exec.d.ts +2 -2
- package/dist/aptos/exec.d.ts.map +1 -1
- package/dist/aptos/exec.js.map +1 -1
- package/dist/aptos/hasher.d.ts.map +1 -1
- package/dist/aptos/hasher.js +1 -1
- package/dist/aptos/hasher.js.map +1 -1
- package/dist/aptos/index.d.ts +17 -16
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +42 -73
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.d.ts +2 -2
- package/dist/aptos/logs.d.ts.map +1 -1
- package/dist/aptos/types.d.ts +2 -19
- package/dist/aptos/types.d.ts.map +1 -1
- package/dist/aptos/types.js +0 -11
- package/dist/aptos/types.js.map +1 -1
- package/dist/chain.d.ts +538 -158
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +132 -19
- package/dist/chain.js.map +1 -1
- package/dist/commits.d.ts +4 -6
- package/dist/commits.d.ts.map +1 -1
- package/dist/commits.js +4 -4
- package/dist/commits.js.map +1 -1
- package/dist/errors/CCIPError.d.ts +33 -4
- package/dist/errors/CCIPError.d.ts.map +1 -1
- package/dist/errors/CCIPError.js +33 -4
- package/dist/errors/CCIPError.js.map +1 -1
- package/dist/errors/codes.d.ts +3 -0
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +3 -1
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +4 -4
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +4 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +4 -1
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +1695 -120
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +1715 -123
- package/dist/errors/specialized.js.map +1 -1
- package/dist/errors/utils.d.ts.map +1 -1
- package/dist/errors/utils.js +0 -1
- package/dist/errors/utils.js.map +1 -1
- package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
- package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
- package/dist/evm/abi/OffRamp_2_0.js +744 -0
- package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
- package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
- package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
- package/dist/evm/abi/OnRamp_2_0.js +992 -0
- package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
- package/dist/evm/const.d.ts +12 -2
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +8 -2
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/errors.d.ts.map +1 -1
- package/dist/evm/errors.js +7 -2
- package/dist/evm/errors.js.map +1 -1
- package/dist/evm/extra-args.d.ts.map +1 -1
- package/dist/evm/extra-args.js +5 -24
- package/dist/evm/extra-args.js.map +1 -1
- package/dist/evm/hasher.d.ts.map +1 -1
- package/dist/evm/hasher.js +23 -13
- package/dist/evm/hasher.js.map +1 -1
- package/dist/evm/index.d.ts +73 -16
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +241 -146
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/logs.d.ts +1 -1
- package/dist/evm/logs.js +1 -1
- package/dist/evm/messages.d.ts +59 -5
- package/dist/evm/messages.d.ts.map +1 -1
- package/dist/evm/messages.js +210 -0
- package/dist/evm/messages.js.map +1 -1
- package/dist/evm/offchain.d.ts +1 -14
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +1 -133
- package/dist/evm/offchain.js.map +1 -1
- package/dist/evm/types.d.ts +7 -2
- package/dist/evm/types.d.ts.map +1 -1
- package/dist/evm/types.js +22 -1
- package/dist/evm/types.js.map +1 -1
- package/dist/execution.d.ts +62 -22
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +98 -61
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +13 -3
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +13 -3
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +25 -2
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +30 -4
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts +23 -6
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +92 -17
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +85 -14
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +99 -17
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +12 -0
- package/dist/selectors.js.map +1 -1
- package/dist/shared/bcs-codecs.d.ts +61 -0
- package/dist/shared/bcs-codecs.d.ts.map +1 -0
- package/dist/shared/bcs-codecs.js +102 -0
- package/dist/shared/bcs-codecs.js.map +1 -0
- package/dist/shared/constants.d.ts +3 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +3 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/solana/exec.d.ts +2 -2
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts +1 -1
- package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js +1 -1
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts +1 -1
- package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js +1 -1
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts +1 -1
- package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js +1 -1
- package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts +16 -1
- package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts.map +1 -1
- package/dist/solana/idl/1.6.0/CCIP_COMMON.js +16 -1
- package/dist/solana/idl/1.6.0/CCIP_COMMON.js.map +1 -1
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts +1 -1
- package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js +1 -1
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts +1 -1
- package/dist/solana/idl/1.6.0/CCIP_ROUTER.js +1 -1
- package/dist/solana/index.d.ts +85 -24
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +69 -37
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.d.ts +1 -13
- package/dist/solana/offchain.d.ts.map +1 -1
- package/dist/solana/offchain.js +1 -66
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/utils.d.ts +4 -4
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +1 -1
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/hasher.d.ts.map +1 -1
- package/dist/sui/hasher.js +1 -1
- package/dist/sui/hasher.js.map +1 -1
- package/dist/sui/index.d.ts +18 -18
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +38 -39
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/manuallyExec/encoder.d.ts +2 -2
- package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
- package/dist/sui/manuallyExec/encoder.js.map +1 -1
- package/dist/sui/manuallyExec/index.d.ts +2 -2
- package/dist/sui/manuallyExec/index.d.ts.map +1 -1
- package/dist/ton/exec.d.ts +3 -3
- package/dist/ton/exec.d.ts.map +1 -1
- package/dist/ton/exec.js +1 -1
- package/dist/ton/exec.js.map +1 -1
- package/dist/ton/index.d.ts +14 -22
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +26 -35
- package/dist/ton/index.js.map +1 -1
- package/dist/ton/types.d.ts +3 -5
- package/dist/ton/types.d.ts.map +1 -1
- package/dist/ton/types.js.map +1 -1
- package/dist/types.d.ts +55 -20
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +65 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +74 -2
- package/dist/utils.js.map +1 -1
- package/package.json +14 -10
- package/src/api/index.ts +53 -17
- package/src/api/types.ts +0 -2
- package/src/aptos/exec.ts +2 -2
- package/src/aptos/hasher.ts +1 -1
- package/src/aptos/index.ts +55 -100
- package/src/aptos/logs.ts +2 -2
- package/src/aptos/types.ts +2 -15
- package/src/chain.ts +594 -171
- package/src/commits.ts +9 -9
- package/src/errors/CCIPError.ts +33 -4
- package/src/errors/codes.ts +3 -1
- package/src/errors/index.ts +4 -0
- package/src/errors/recovery.ts +7 -1
- package/src/errors/specialized.ts +1726 -130
- package/src/errors/utils.ts +0 -1
- package/src/evm/abi/OffRamp_2_0.ts +743 -0
- package/src/evm/abi/OnRamp_2_0.ts +991 -0
- package/src/evm/const.ts +10 -3
- package/src/evm/errors.ts +6 -2
- package/src/evm/extra-args.ts +4 -21
- package/src/evm/hasher.ts +30 -18
- package/src/evm/index.ts +314 -176
- package/src/evm/logs.ts +1 -1
- package/src/evm/messages.ts +323 -11
- package/src/evm/offchain.ts +2 -191
- package/src/evm/types.ts +20 -2
- package/src/execution.ts +125 -86
- package/src/extra-args.ts +13 -3
- package/src/gas.ts +29 -3
- package/src/index.ts +10 -3
- package/src/offchain.ts +125 -28
- package/src/requests.ts +114 -19
- package/src/selectors.ts +12 -0
- package/src/shared/bcs-codecs.ts +132 -0
- package/src/shared/constants.ts +2 -0
- package/src/solana/exec.ts +4 -4
- package/src/solana/idl/1.6.0/BASE_TOKEN_POOL.ts +2 -2
- package/src/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.ts +2 -2
- package/src/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts +2 -2
- package/src/solana/idl/1.6.0/CCIP_COMMON.ts +32 -2
- package/src/solana/idl/1.6.0/CCIP_OFFRAMP.ts +2 -2
- package/src/solana/idl/1.6.0/CCIP_ROUTER.ts +2 -2
- package/src/solana/index.ts +110 -85
- package/src/solana/offchain.ts +3 -100
- package/src/solana/utils.ts +8 -5
- package/src/sui/hasher.ts +1 -1
- package/src/sui/index.ts +55 -59
- package/src/sui/manuallyExec/encoder.ts +2 -2
- package/src/sui/manuallyExec/index.ts +2 -2
- package/src/ton/exec.ts +4 -7
- package/src/ton/index.ts +45 -53
- package/src/ton/types.ts +4 -7
- package/src/types.ts +81 -37
- package/src/utils.ts +73 -2
- package/dist/aptos/utils.d.ts +0 -12
- package/dist/aptos/utils.d.ts.map +0 -1
- package/dist/aptos/utils.js +0 -15
- package/dist/aptos/utils.js.map +0 -1
- package/src/aptos/utils.ts +0 -24
package/src/execution.ts
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
import { memoize } from 'micro-memoize'
|
|
2
2
|
|
|
3
|
-
import type { Chain
|
|
3
|
+
import type { Chain } from './chain.ts'
|
|
4
4
|
import {
|
|
5
5
|
CCIPMerkleRootMismatchError,
|
|
6
6
|
CCIPMessageNotInBatchError,
|
|
7
7
|
CCIPOffRampNotFoundError,
|
|
8
8
|
} from './errors/index.ts'
|
|
9
9
|
import { Tree, getLeafHasher, proofFlagsToBits } from './hasher/index.ts'
|
|
10
|
-
import {
|
|
11
|
-
type CCIPCommit,
|
|
12
|
-
type CCIPExecution,
|
|
13
|
-
type CCIPMessage,
|
|
14
|
-
type CCIPRequest,
|
|
15
|
-
type CCIPVersion,
|
|
16
|
-
type ExecutionReport,
|
|
17
|
-
type Lane,
|
|
18
|
-
type WithLogger,
|
|
19
|
-
ExecutionState,
|
|
20
|
-
} from './types.ts'
|
|
10
|
+
import type { CCIPMessage, CCIPVersion, Lane, WithLogger } from './types.ts'
|
|
21
11
|
|
|
22
12
|
/**
|
|
23
13
|
* Pure/sync function to calculate/generate OffRamp.executeManually report for messageIds
|
|
@@ -28,6 +18,38 @@ import {
|
|
|
28
18
|
* @param merkleRoot - Optional merkleRoot of the CommitReport, for validation
|
|
29
19
|
* @param ctx - Context for logging
|
|
30
20
|
* @returns ManualExec report arguments
|
|
21
|
+
* @throws CCIPMessageNotInBatchError - When the messageId is not found in the provided batch
|
|
22
|
+
* @throws CCIPMerkleRootMismatchError - When calculated merkle root doesn't match the provided one
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* This is a pure/sync function that performs no I/O - all data must be pre-fetched.
|
|
26
|
+
* It builds a merkle tree from the messages, generates a proof for the target messageId,
|
|
27
|
+
* and optionally validates against the provided merkleRoot.
|
|
28
|
+
*
|
|
29
|
+
* The returned proof can be used with `execute` to manually execute a stuck message.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { calculateManualExecProof, EVMChain } from '@chainlink/ccip-sdk'
|
|
34
|
+
*
|
|
35
|
+
* // Fetch the request and all messages in its batch
|
|
36
|
+
* const request = (await source.getMessagesInTx(txHash))[0]
|
|
37
|
+
* const verifications = await dest.getVerifications({ offRamp, request })
|
|
38
|
+
* const messages = await source.getMessagesInBatch(request, commit.report)
|
|
39
|
+
*
|
|
40
|
+
* // Calculate proof for manual execution
|
|
41
|
+
* const proof = calculateManualExecProof(
|
|
42
|
+
* messages,
|
|
43
|
+
* request.lane,
|
|
44
|
+
* request.message.messageId,
|
|
45
|
+
* commit.report.merkleRoot
|
|
46
|
+
* )
|
|
47
|
+
* console.log('Merkle root:', proof.merkleRoot)
|
|
48
|
+
* console.log('Proofs:', proof.proofs)
|
|
49
|
+
* ```
|
|
50
|
+
* @see {@link discoverOffRamp} - Find the OffRamp for manual execution
|
|
51
|
+
* @see {@link execute} - Execute the report on destination chain
|
|
52
|
+
* @see {@link generateUnsignedExecute} - Build unsigned execution tx
|
|
31
53
|
**/
|
|
32
54
|
export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
|
|
33
55
|
messagesInBatch: readonly CCIPMessage<V>[],
|
|
@@ -35,7 +57,7 @@ export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
|
|
|
35
57
|
messageId: string,
|
|
36
58
|
merkleRoot?: string,
|
|
37
59
|
ctx?: WithLogger,
|
|
38
|
-
)
|
|
60
|
+
) {
|
|
39
61
|
const hasher = getLeafHasher(lane, ctx)
|
|
40
62
|
|
|
41
63
|
const msgIdx = messagesInBatch.findIndex((message) => message.messageId === messageId)
|
|
@@ -64,6 +86,30 @@ export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
|
|
|
64
86
|
}
|
|
65
87
|
}
|
|
66
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Discover the OffRamp address for a given OnRamp and destination chain.
|
|
91
|
+
* Results are memoized for performance.
|
|
92
|
+
*
|
|
93
|
+
* @param source - Source chain instance.
|
|
94
|
+
* @param dest - Destination chain instance.
|
|
95
|
+
* @param onRamp - OnRamp contract address on source chain.
|
|
96
|
+
* @param ctx - Optional context with logger.
|
|
97
|
+
* @returns OffRamp address on destination chain.
|
|
98
|
+
* @throws CCIPOffRampNotFoundError - When no matching OffRamp is found for the OnRamp
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* import { discoverOffRamp, EVMChain } from '@chainlink/ccip-sdk'
|
|
102
|
+
*
|
|
103
|
+
* const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
|
|
104
|
+
* const dest = await EVMChain.fromUrl('https://rpc.fuji.avax.network')
|
|
105
|
+
*
|
|
106
|
+
* const offRamp = await discoverOffRamp(source, dest, onRampAddress)
|
|
107
|
+
* console.log('OffRamp on destination:', offRamp)
|
|
108
|
+
* ```
|
|
109
|
+
* @see {@link calculateManualExecProof} - Use with OffRamp for manual execution
|
|
110
|
+
* @see {@link execute} - Execute on destination chain
|
|
111
|
+
* @see {@link getExecutionReceipts} - Check execution status
|
|
112
|
+
*/
|
|
67
113
|
export const discoverOffRamp = memoize(
|
|
68
114
|
async function discoverOffRamp_(
|
|
69
115
|
source: Chain,
|
|
@@ -77,42 +123,76 @@ export const discoverOffRamp = memoize(
|
|
|
77
123
|
dest.network.chainSelector,
|
|
78
124
|
)
|
|
79
125
|
for (const offRamp of sourceOffRamps) {
|
|
80
|
-
let
|
|
126
|
+
let destOnRamps
|
|
81
127
|
try {
|
|
82
|
-
|
|
83
|
-
} catch {
|
|
84
|
-
logger.debug('discoverOffRamp: skipping offRamp', offRamp, '(no valid source chain config)')
|
|
85
|
-
continue
|
|
86
|
-
}
|
|
87
|
-
const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
|
|
88
|
-
const destOffRamps = await dest.getOffRampsForRouter(destRouter, source.network.chainSelector)
|
|
89
|
-
for (const offRamp of destOffRamps) {
|
|
90
|
-
let offRampsOnRamp: string
|
|
91
|
-
try {
|
|
92
|
-
offRampsOnRamp = await dest.getOnRampForOffRamp(offRamp, source.network.chainSelector)
|
|
93
|
-
} catch {
|
|
94
|
-
logger.debug(
|
|
95
|
-
'discoverOffRamp: skipping dest offRamp',
|
|
96
|
-
offRamp,
|
|
97
|
-
'(no valid source chain config)',
|
|
98
|
-
)
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
128
|
+
destOnRamps = await source.getOnRampsForOffRamp(offRamp, dest.network.chainSelector)
|
|
129
|
+
} catch (err) {
|
|
101
130
|
logger.debug(
|
|
102
|
-
'discoverOffRamp:
|
|
103
|
-
{
|
|
104
|
-
sourceOnRamp: onRamp,
|
|
105
|
-
sourceRouter,
|
|
106
|
-
sourceOffRamps,
|
|
107
|
-
destOnRamp,
|
|
108
|
-
destOffRamps,
|
|
109
|
-
offRampsOnRamp,
|
|
110
|
-
},
|
|
111
|
-
'=',
|
|
131
|
+
'discoverOffRamp: skipping offRamp',
|
|
112
132
|
offRamp,
|
|
133
|
+
'(no valid source chain config)',
|
|
134
|
+
err,
|
|
135
|
+
)
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
for (const destOnRamp of destOnRamps) {
|
|
139
|
+
const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
|
|
140
|
+
const destOffRamps = await dest.getOffRampsForRouter(
|
|
141
|
+
destRouter,
|
|
142
|
+
source.network.chainSelector,
|
|
113
143
|
)
|
|
114
|
-
|
|
115
|
-
|
|
144
|
+
for (const offRamp of destOffRamps) {
|
|
145
|
+
let offRampsOnRamps
|
|
146
|
+
try {
|
|
147
|
+
offRampsOnRamps = await dest.getOnRampsForOffRamp(offRamp, source.network.chainSelector)
|
|
148
|
+
} catch (err) {
|
|
149
|
+
logger.debug(
|
|
150
|
+
'discoverOffRamp: skipping dest offRamp',
|
|
151
|
+
offRamp,
|
|
152
|
+
'(no valid source chain config)',
|
|
153
|
+
err,
|
|
154
|
+
)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
for (const offRampsOnRamp of offRampsOnRamps) {
|
|
158
|
+
logger.debug(
|
|
159
|
+
'discoverOffRamp: found, from',
|
|
160
|
+
{
|
|
161
|
+
sourceOnRamp: onRamp,
|
|
162
|
+
sourceRouter,
|
|
163
|
+
sourceOffRamps,
|
|
164
|
+
destOnRamp,
|
|
165
|
+
destOffRamps,
|
|
166
|
+
offRampsOnRamp,
|
|
167
|
+
},
|
|
168
|
+
'=',
|
|
169
|
+
offRamp,
|
|
170
|
+
)
|
|
171
|
+
for (const offRamp of destOffRamps) {
|
|
172
|
+
const offRampsOnRamps = await dest.getOnRampsForOffRamp(
|
|
173
|
+
offRamp,
|
|
174
|
+
source.network.chainSelector,
|
|
175
|
+
)
|
|
176
|
+
for (const offRampsOnRamp of offRampsOnRamps) {
|
|
177
|
+
logger.debug(
|
|
178
|
+
'discoverOffRamp: found, from',
|
|
179
|
+
{
|
|
180
|
+
sourceOnRamp: onRamp,
|
|
181
|
+
sourceRouter,
|
|
182
|
+
sourceOffRamps,
|
|
183
|
+
destOnRamp,
|
|
184
|
+
destOffRamps,
|
|
185
|
+
offRampsOnRamps,
|
|
186
|
+
},
|
|
187
|
+
'=',
|
|
188
|
+
offRamp,
|
|
189
|
+
)
|
|
190
|
+
if (offRampsOnRamp === onRamp) {
|
|
191
|
+
return offRamp
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
116
196
|
}
|
|
117
197
|
}
|
|
118
198
|
}
|
|
@@ -123,44 +203,3 @@ export const discoverOffRamp = memoize(
|
|
|
123
203
|
[source.network.chainSelector, dest.network.chainSelector, onRamp] as const,
|
|
124
204
|
},
|
|
125
205
|
)
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Generic implementation for fetching ExecutionReceipts for given requests.
|
|
129
|
-
* If more than one request is given, may yield them interleaved.
|
|
130
|
-
* Completes as soon as there's no more work to be done.
|
|
131
|
-
*
|
|
132
|
-
* Two possible behaviors:
|
|
133
|
-
* - if `startBlock|startTime` is given, pages forward from that block up;
|
|
134
|
-
* completes when success (final) receipt is found for all requests (or reach latest block)
|
|
135
|
-
* - otherwise, pages backwards and returns only the most recent receipt per request;
|
|
136
|
-
* completes when receipts for all requests were seen
|
|
137
|
-
*
|
|
138
|
-
* @param dest - Provider to page through.
|
|
139
|
-
* @param offRamp - OffRamp contract address.
|
|
140
|
-
* @param request - CCIP request to search executions for.
|
|
141
|
-
* @param commit - Optional commit info to narrow down search.
|
|
142
|
-
* @param hints - Optional hints (e.g., `page` for getLogs pagination range).
|
|
143
|
-
*/
|
|
144
|
-
export async function* getExecutionReceipts(
|
|
145
|
-
dest: Chain,
|
|
146
|
-
offRamp: string,
|
|
147
|
-
request: CCIPRequest,
|
|
148
|
-
commit?: CCIPCommit,
|
|
149
|
-
hints?: { page?: number },
|
|
150
|
-
): AsyncGenerator<CCIPExecution> {
|
|
151
|
-
const onlyLast = !commit?.log.blockNumber && !request.tx.timestamp // backwards
|
|
152
|
-
for await (const log of dest.getLogs({
|
|
153
|
-
startBlock: commit?.log.blockNumber,
|
|
154
|
-
startTime: request.tx.timestamp,
|
|
155
|
-
address: offRamp,
|
|
156
|
-
topics: ['ExecutionStateChanged'],
|
|
157
|
-
...hints,
|
|
158
|
-
})) {
|
|
159
|
-
const receipt = (dest.constructor as ChainStatic).decodeReceipt(log)
|
|
160
|
-
if (!receipt || receipt.messageId !== request.message.messageId) continue
|
|
161
|
-
|
|
162
|
-
const timestamp = log.tx?.timestamp ?? (await dest.getBlockTimestamp(log.blockNumber))
|
|
163
|
-
yield { receipt, log, timestamp }
|
|
164
|
-
if (onlyLast || receipt.state === ExecutionState.Success) break
|
|
165
|
-
}
|
|
166
|
-
}
|
package/src/extra-args.ts
CHANGED
|
@@ -9,7 +9,7 @@ export const EVMExtraArgsV1Tag = id('CCIP EVMExtraArgsV1').substring(0, 10) as '
|
|
|
9
9
|
/** Tag identifier for EVMExtraArgsV2 encoding. */
|
|
10
10
|
export const EVMExtraArgsV2Tag = id('CCIP EVMExtraArgsV2').substring(0, 10) as '0x181dcf10'
|
|
11
11
|
/** Tag identifier for GenericExtraArgsV3 encoding (tightly packed binary format). */
|
|
12
|
-
export const GenericExtraArgsV3Tag = '
|
|
12
|
+
export const GenericExtraArgsV3Tag = id('CCIP GenericExtraArgsV3').substring(0, 10) as '0xa69dd4aa'
|
|
13
13
|
/** Tag identifier for SVMExtraArgsV1 encoding. */
|
|
14
14
|
export const SVMExtraArgsV1Tag = id('CCIP SVMExtraArgsV1').substring(0, 10) as '0x1f3b3aba'
|
|
15
15
|
/** Tag identifier for SuiExtraArgsV1 encoding. */
|
|
@@ -145,6 +145,7 @@ export type ExtraArgs =
|
|
|
145
145
|
* Encodes extra arguments for CCIP messages.
|
|
146
146
|
* The args are *to* a dest network, but are encoded as a message *from* this source chain.
|
|
147
147
|
* E.g. Solana uses Borsh to encode extraArgs in its produced requests, even those targeting EVM.
|
|
148
|
+
*
|
|
148
149
|
* @param args - Extra arguments to encode
|
|
149
150
|
* @param from - Source chain family for encoding format (defaults to EVM)
|
|
150
151
|
* @returns Encoded extra arguments as hex string
|
|
@@ -152,12 +153,16 @@ export type ExtraArgs =
|
|
|
152
153
|
*
|
|
153
154
|
* @example
|
|
154
155
|
* ```typescript
|
|
156
|
+
* import { encodeExtraArgs } from '@chainlink/ccip-sdk'
|
|
157
|
+
*
|
|
155
158
|
* const encoded = encodeExtraArgs({
|
|
156
159
|
* gasLimit: 200_000n,
|
|
157
160
|
* allowOutOfOrderExecution: true,
|
|
158
161
|
* })
|
|
159
|
-
* //
|
|
162
|
+
* console.log('Encoded:', encoded) // '0x181dcf10...'
|
|
160
163
|
* ```
|
|
164
|
+
*
|
|
165
|
+
* @see {@link decodeExtraArgs} - Decode extra arguments from bytes
|
|
161
166
|
*/
|
|
162
167
|
export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily.EVM): string {
|
|
163
168
|
const chain = supportedChains[from]
|
|
@@ -175,11 +180,16 @@ export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily
|
|
|
175
180
|
*
|
|
176
181
|
* @example
|
|
177
182
|
* ```typescript
|
|
183
|
+
* import { decodeExtraArgs } from '@chainlink/ccip-sdk'
|
|
184
|
+
*
|
|
178
185
|
* const decoded = decodeExtraArgs('0x181dcf10...')
|
|
179
186
|
* if (decoded?._tag === 'EVMExtraArgsV2') {
|
|
180
|
-
* console.log(
|
|
187
|
+
* console.log('Gas limit:', decoded.gasLimit)
|
|
188
|
+
* console.log('Out of order:', decoded.allowOutOfOrderExecution)
|
|
181
189
|
* }
|
|
182
190
|
* ```
|
|
191
|
+
*
|
|
192
|
+
* @see {@link encodeExtraArgs} - Encode extra arguments to bytes
|
|
183
193
|
*/
|
|
184
194
|
export function decodeExtraArgs(
|
|
185
195
|
data: BytesLike,
|
package/src/gas.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { Chain } from './chain.ts'
|
|
|
4
4
|
import {
|
|
5
5
|
CCIPContractTypeInvalidError,
|
|
6
6
|
CCIPMethodUnsupportedError,
|
|
7
|
+
CCIPOnRampRequiredError,
|
|
7
8
|
CCIPTokenDecimalsInsufficientError,
|
|
8
9
|
} from './errors/index.ts'
|
|
9
10
|
import { discoverOffRamp } from './execution.ts'
|
|
@@ -50,11 +51,34 @@ export type EstimateReceiveExecutionOpts = {
|
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* Estimate CCIP gasLimit needed to execute a request on a contract receiver.
|
|
53
|
-
*
|
|
54
|
-
* @
|
|
54
|
+
*
|
|
55
|
+
* @param opts - {@link EstimateReceiveExecutionOpts} for estimation
|
|
56
|
+
* @returns Estimated gasLimit
|
|
57
|
+
*
|
|
55
58
|
* @throws {@link CCIPMethodUnsupportedError} if dest chain doesn't support estimation
|
|
56
59
|
* @throws {@link CCIPContractTypeInvalidError} if routerOrRamp is not a valid contract type
|
|
57
60
|
* @throws {@link CCIPTokenDecimalsInsufficientError} if dest token has insufficient decimals
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { estimateReceiveExecution, EVMChain } from '@chainlink/ccip-sdk'
|
|
65
|
+
*
|
|
66
|
+
* const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
|
|
67
|
+
* const dest = await EVMChain.fromUrl('https://rpc.fuji.avax.network')
|
|
68
|
+
*
|
|
69
|
+
* const gasLimit = await estimateReceiveExecution({
|
|
70
|
+
* source,
|
|
71
|
+
* dest,
|
|
72
|
+
* routerOrRamp: '0xRouter...',
|
|
73
|
+
* message: {
|
|
74
|
+
* sender: '0x...',
|
|
75
|
+
* receiver: '0x...',
|
|
76
|
+
* data: '0x...',
|
|
77
|
+
* tokenAmounts: [],
|
|
78
|
+
* },
|
|
79
|
+
* })
|
|
80
|
+
* console.log('Estimated gas:', gasLimit)
|
|
81
|
+
* ```
|
|
58
82
|
*/
|
|
59
83
|
export async function estimateReceiveExecution({
|
|
60
84
|
source,
|
|
@@ -78,7 +102,9 @@ export async function estimateReceiveExecution({
|
|
|
78
102
|
if (!tnv[0].includes('OffRamp'))
|
|
79
103
|
throw new CCIPContractTypeInvalidError(routerOrRamp, tnv[2], ['OffRamp'])
|
|
80
104
|
offRamp = routerOrRamp
|
|
81
|
-
|
|
105
|
+
const onRamps = await dest.getOnRampsForOffRamp(offRamp, source.network.chainSelector)
|
|
106
|
+
if (!onRamps.length) throw new CCIPOnRampRequiredError()
|
|
107
|
+
onRamp = onRamps[onRamps.length - 1]!
|
|
82
108
|
} catch {
|
|
83
109
|
throw sourceErr // re-throw original error
|
|
84
110
|
}
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,12 @@ export type {
|
|
|
13
13
|
CCIPAPIClientContext,
|
|
14
14
|
LaneLatencyResponse,
|
|
15
15
|
} from './api/index.ts'
|
|
16
|
-
export {
|
|
16
|
+
export {
|
|
17
|
+
CCIPAPIClient,
|
|
18
|
+
DEFAULT_API_BASE_URL,
|
|
19
|
+
SDK_VERSION,
|
|
20
|
+
SDK_VERSION_HEADER,
|
|
21
|
+
} from './api/index.ts'
|
|
17
22
|
|
|
18
23
|
export type {
|
|
19
24
|
ApiRetryConfig,
|
|
@@ -41,16 +46,18 @@ export {
|
|
|
41
46
|
encodeExtraArgs,
|
|
42
47
|
} from './extra-args.ts'
|
|
43
48
|
export { estimateReceiveExecution } from './gas.ts'
|
|
49
|
+
export { getOffchainTokenData } from './offchain.ts'
|
|
44
50
|
export { decodeMessage, getMessagesForSender, sourceToDestTokenAddresses } from './requests.ts'
|
|
45
51
|
export {
|
|
46
|
-
type CCIPCommit,
|
|
47
52
|
type CCIPExecution,
|
|
48
53
|
type CCIPMessage,
|
|
49
54
|
type CCIPRequest,
|
|
55
|
+
type CCIPVerifications,
|
|
56
|
+
type ChainLog,
|
|
50
57
|
type ChainTransaction,
|
|
51
58
|
type CommitReport,
|
|
59
|
+
type ExecutionInput,
|
|
52
60
|
type ExecutionReceipt,
|
|
53
|
-
type ExecutionReport,
|
|
54
61
|
type Lane,
|
|
55
62
|
type Logger,
|
|
56
63
|
type MessageInput,
|
package/src/offchain.ts
CHANGED
|
@@ -1,56 +1,79 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type BytesLike, dataLength, dataSlice, getBytes, toNumber } from 'ethers'
|
|
2
|
+
import type { PickDeep } from 'type-fest'
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
CCIPLbtcAttestationNotApprovedError,
|
|
5
6
|
CCIPLbtcAttestationNotFoundError,
|
|
6
7
|
CCIPUsdcAttestationError,
|
|
7
8
|
} from './errors/index.ts'
|
|
8
|
-
import {
|
|
9
|
+
import { parseSourceTokenData } from './evm/messages.ts'
|
|
10
|
+
import { type CCIPRequest, type OffchainTokenData, type WithLogger, NetworkType } from './types.ts'
|
|
11
|
+
import { networkInfo } from './utils.ts'
|
|
9
12
|
|
|
10
13
|
const CIRCLE_API_URL = {
|
|
11
|
-
mainnet: 'https://iris-api.circle.com
|
|
12
|
-
testnet: 'https://iris-api-sandbox.circle.com
|
|
14
|
+
mainnet: 'https://iris-api.circle.com',
|
|
15
|
+
testnet: 'https://iris-api-sandbox.circle.com',
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
type CctpAttestationResponse =
|
|
16
19
|
| { error: 'string' }
|
|
17
|
-
| {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type LombardAttestation =
|
|
26
|
-
| { status: 'NOTARIZATION_STATUS_SESSION_APPROVED'; message_hash: string; attestation: string }
|
|
27
|
-
| { status: string; message_hash: string }
|
|
28
|
-
type LombardAttestationsResponse = { attestations: Array<LombardAttestation> }
|
|
20
|
+
| {
|
|
21
|
+
messages: {
|
|
22
|
+
status: 'pending_confirmations' | 'complete'
|
|
23
|
+
eventNonce?: string
|
|
24
|
+
attestation: string
|
|
25
|
+
message: string
|
|
26
|
+
}[]
|
|
27
|
+
}
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
|
-
* Returns the USDC attestation for a given
|
|
32
|
-
* https://developers.circle.com/
|
|
30
|
+
* Returns the USDC attestation for a given tokenAmount.extraData and txHash
|
|
31
|
+
* https://developers.circle.com/cctp/quickstarts/transfer-usdc-ethereum-to-arc#3-3-retrieve-attestation
|
|
33
32
|
*
|
|
34
|
-
* @param
|
|
33
|
+
* @param opts - CCTPv2 options
|
|
35
34
|
* @param networkType - network type (mainnet or testnet)
|
|
36
|
-
* @returns USDC/CCTP attestation
|
|
35
|
+
* @returns USDC/CCTP attestation and message
|
|
37
36
|
*/
|
|
38
37
|
export async function getUsdcAttestation(
|
|
39
|
-
|
|
38
|
+
opts: {
|
|
39
|
+
/** CCTP sourceDomain */
|
|
40
|
+
sourceDomain: number
|
|
41
|
+
/** CCTP burn eventNonce */
|
|
42
|
+
nonce: number
|
|
43
|
+
/** burn txHash, same as CCIP request */
|
|
44
|
+
txHash: string
|
|
45
|
+
},
|
|
40
46
|
networkType: NetworkType,
|
|
41
|
-
): Promise<string> {
|
|
42
|
-
const
|
|
43
|
-
|
|
47
|
+
): Promise<{ attestation: string; message: string }> {
|
|
48
|
+
const { sourceDomain, nonce, txHash } = opts
|
|
44
49
|
const circleApiBaseUrl =
|
|
45
50
|
networkType === NetworkType.Mainnet ? CIRCLE_API_URL.mainnet : CIRCLE_API_URL.testnet
|
|
46
|
-
const res = await fetch(
|
|
51
|
+
const res = await fetch(
|
|
52
|
+
`${circleApiBaseUrl}/v2/messages/${sourceDomain}?transactionHash=${txHash}`,
|
|
53
|
+
)
|
|
47
54
|
const json = (await res.json()) as CctpAttestationResponse
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
let att
|
|
56
|
+
if ('messages' in json) {
|
|
57
|
+
att = json.messages.find((m) => m.status === 'complete' && m.eventNonce === nonce.toString())
|
|
50
58
|
}
|
|
51
|
-
|
|
59
|
+
if (!att?.message) throw new CCIPUsdcAttestationError(txHash, json, { context: opts })
|
|
60
|
+
return att
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const LOMBARD_API_URL = {
|
|
64
|
+
mainnet: 'https://mainnet.prod.lombard.finance',
|
|
65
|
+
testnet: 'https://gastald-testnet.prod.lombard.finance',
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
type LombardAttestation =
|
|
69
|
+
| {
|
|
70
|
+
status: 'NOTARIZATION_STATUS_SESSION_APPROVED'
|
|
71
|
+
message_hash: string
|
|
72
|
+
attestation: string
|
|
73
|
+
}
|
|
74
|
+
| { status: string; message_hash: string }
|
|
75
|
+
type LombardAttestationsResponse = { attestations: Array<LombardAttestation> }
|
|
76
|
+
|
|
54
77
|
/**
|
|
55
78
|
* Returns the LBTC attestation for a given payload hash
|
|
56
79
|
*
|
|
@@ -86,3 +109,77 @@ export async function getLbtcAttestation(
|
|
|
86
109
|
}
|
|
87
110
|
return attestation
|
|
88
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Fetch CCIPv1 offchain token data for USDC and LBTC tokenAmounts
|
|
115
|
+
* @param request - CCIPRequest containing tx.hash and message
|
|
116
|
+
* @returns Promise resolving to an OffchainTokenData for each tokenAmount
|
|
117
|
+
*/
|
|
118
|
+
export async function getOffchainTokenData(
|
|
119
|
+
request: PickDeep<CCIPRequest, 'tx.hash' | `message`>,
|
|
120
|
+
{ logger = console }: WithLogger = {},
|
|
121
|
+
): Promise<OffchainTokenData[]> {
|
|
122
|
+
const { networkType } = networkInfo(request.message.sourceChainSelector)
|
|
123
|
+
|
|
124
|
+
function looksUsdcData(extraData: BytesLike) {
|
|
125
|
+
if (dataLength(extraData) !== 64) return
|
|
126
|
+
// USDCTokenPool's extraData is a packed `SourceTokenDataPayloadV1{uint64 nonce, uint32 sourceDomain}`,
|
|
127
|
+
// which we need to query CCTPv2 (by sourceDomain and txHash) and to filter by nonce among messages,
|
|
128
|
+
// if more than one in tx
|
|
129
|
+
let nonce, sourceDomain
|
|
130
|
+
try {
|
|
131
|
+
// those toNumber conversions throw early in case the bytearray don't look like small numbers
|
|
132
|
+
nonce = toNumber(dataSlice(extraData, 0, 32))
|
|
133
|
+
sourceDomain = toNumber(dataSlice(extraData, 32, 32 + 32))
|
|
134
|
+
return { nonce, sourceDomain } // maybe USDC
|
|
135
|
+
} catch {
|
|
136
|
+
// not USDC
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function looksLbtcData(extraData: BytesLike) {
|
|
141
|
+
// LBTC returns `message_hash`/`payloadHash` directly as `bytes32 extraData`
|
|
142
|
+
if (
|
|
143
|
+
dataLength(extraData) === 32 &&
|
|
144
|
+
getBytes(extraData, 'extraData').filter(Boolean).length > 20 // looks like a hash
|
|
145
|
+
)
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return Promise.all(
|
|
150
|
+
request.message.tokenAmounts.map(async (tokenAmount, i) => {
|
|
151
|
+
let extraData
|
|
152
|
+
if ('extraData' in tokenAmount) {
|
|
153
|
+
extraData = tokenAmount.extraData
|
|
154
|
+
} else if ('sourceTokenData' in request.message) {
|
|
155
|
+
// v1.2..v1.5
|
|
156
|
+
if (dataLength(request.message.sourceTokenData[i]!) === 64) {
|
|
157
|
+
extraData = request.message.sourceTokenData[i]
|
|
158
|
+
} else {
|
|
159
|
+
;({ extraData } = parseSourceTokenData(request.message.sourceTokenData[i]!))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!extraData) return
|
|
163
|
+
const usdcOpts = looksUsdcData(extraData)
|
|
164
|
+
if (usdcOpts) {
|
|
165
|
+
try {
|
|
166
|
+
const usdcAttestation = await getUsdcAttestation(
|
|
167
|
+
{ ...usdcOpts, txHash: request.tx.hash },
|
|
168
|
+
networkType,
|
|
169
|
+
)
|
|
170
|
+
return { _tag: 'usdc', extraData, ...usdcAttestation }
|
|
171
|
+
} catch (err) {
|
|
172
|
+
// maybe not a USDC transfer, or not ready
|
|
173
|
+
logger.warn(`❌ CCTP: Failed to fetch attestation for message:`, request.message, err)
|
|
174
|
+
}
|
|
175
|
+
} else if (looksLbtcData(extraData)) {
|
|
176
|
+
try {
|
|
177
|
+
const lbtcAttestation = await getLbtcAttestation(extraData, networkType)
|
|
178
|
+
return { _tag: 'lbtc', extraData, ...lbtcAttestation }
|
|
179
|
+
} catch (err) {
|
|
180
|
+
logger.warn(`❌ LBTC: Failed to fetch attestation for message:`, extraData, err)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
}
|