@chainlink/ccip-sdk 0.95.0 → 0.97.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 +2 -2
- package/dist/all-chains.d.ts +23 -0
- package/dist/all-chains.d.ts.map +1 -0
- package/dist/all-chains.js +24 -0
- package/dist/all-chains.js.map +1 -0
- package/dist/api/index.d.ts +31 -19
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +46 -25
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +24 -30
- 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 +43 -15
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +112 -105
- package/dist/aptos/index.js.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 +734 -174
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +216 -31
- 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 +5 -0
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +5 -1
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +6 -1
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +1702 -121
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +1729 -125
- 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 +25 -0
- package/dist/evm/extra-args.d.ts.map +1 -0
- package/dist/evm/extra-args.js +309 -0
- package/dist/evm/extra-args.js.map +1 -0
- package/dist/evm/gas.d.ts.map +1 -1
- package/dist/evm/gas.js +7 -12
- package/dist/evm/gas.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 +140 -35
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +306 -226
- package/dist/evm/index.js.map +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.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 +102 -51
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +113 -4
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +38 -3
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +31 -5
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +43 -9
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +101 -22
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +115 -24
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +24 -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/index.d.ts +148 -30
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +137 -44
- package/dist/solana/index.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 +49 -19
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +76 -43
- 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 +2 -2
- package/dist/ton/exec.d.ts.map +1 -1
- package/dist/ton/exec.js.map +1 -1
- package/dist/ton/index.d.ts +66 -27
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +172 -47
- package/dist/ton/index.js.map +1 -1
- package/dist/ton/send.d.ts +52 -0
- package/dist/ton/send.d.ts.map +1 -0
- package/dist/ton/send.js +166 -0
- package/dist/ton/send.js.map +1 -0
- package/dist/ton/types.d.ts +2 -2
- package/dist/ton/types.d.ts.map +1 -1
- package/dist/ton/types.js.map +1 -1
- package/dist/types.d.ts +148 -12
- 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 +79 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +92 -7
- package/dist/utils.js.map +1 -1
- package/package.json +16 -11
- package/src/all-chains.ts +26 -0
- package/src/api/index.ts +58 -34
- package/src/api/types.ts +24 -31
- package/src/aptos/exec.ts +2 -2
- package/src/aptos/hasher.ts +1 -1
- package/src/aptos/index.ts +127 -129
- package/src/aptos/types.ts +2 -15
- package/src/chain.ts +837 -191
- package/src/commits.ts +9 -9
- package/src/errors/CCIPError.ts +33 -4
- package/src/errors/codes.ts +5 -1
- package/src/errors/index.ts +2 -1
- package/src/errors/recovery.ts +9 -1
- package/src/errors/specialized.ts +1745 -132
- 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 +360 -0
- package/src/evm/gas.ts +14 -13
- package/src/evm/hasher.ts +30 -18
- package/src/evm/index.ts +376 -281
- package/src/evm/messages.ts +323 -11
- package/src/evm/offchain.ts +2 -2
- package/src/evm/types.ts +20 -2
- package/src/execution.ts +126 -71
- package/src/extra-args.ts +118 -4
- package/src/gas.ts +44 -11
- package/src/index.ts +14 -11
- package/src/requests.ts +128 -24
- package/src/selectors.ts +24 -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/index.ts +170 -82
- package/src/sui/hasher.ts +1 -1
- package/src/sui/index.ts +88 -56
- package/src/sui/manuallyExec/encoder.ts +2 -2
- package/src/sui/manuallyExec/index.ts +2 -2
- package/src/ton/exec.ts +2 -2
- package/src/ton/index.ts +220 -58
- package/src/ton/send.ts +222 -0
- package/src/ton/types.ts +2 -2
- package/src/types.ts +173 -30
- package/src/utils.ts +91 -7
- 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/dist/evm/index.js
CHANGED
|
@@ -1,48 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Contract, JsonRpcProvider, Result, WebSocketProvider, ZeroAddress, concat, dataSlice, encodeBase58, getAddress, hexlify, isBytesLike, isHexString, toBeHex, toBigInt, zeroPadValue, } from 'ethers';
|
|
1
|
+
import { Contract, JsonRpcProvider, WebSocketProvider, ZeroAddress, getAddress, hexlify, isBytesLike, isHexString, keccak256, toBeHex, zeroPadValue, } from 'ethers';
|
|
3
2
|
import { memoize } from 'micro-memoize';
|
|
4
3
|
import { Chain, } from "../chain.js";
|
|
5
|
-
import { CCIPAddressInvalidEvmError, CCIPBlockNotFoundError, CCIPContractNotRouterError, CCIPContractTypeInvalidError, CCIPDataFormatUnsupportedError, CCIPExecTxNotConfirmedError, CCIPExecTxRevertedError,
|
|
6
|
-
import { EVMExtraArgsV1Tag, EVMExtraArgsV2Tag, SVMExtraArgsV1Tag, SuiExtraArgsV1Tag, } from "../extra-args.js";
|
|
4
|
+
import { CCIPAddressInvalidEvmError, CCIPApiClientNotAvailableError, CCIPBlockNotFoundError, CCIPContractNotRouterError, CCIPContractTypeInvalidError, CCIPDataFormatUnsupportedError, CCIPExecTxNotConfirmedError, CCIPExecTxRevertedError, CCIPHasherVersionUnsupportedError, CCIPLogDataInvalidError, CCIPNotImplementedError, CCIPSourceChainUnsupportedError, CCIPTokenNotConfiguredError, CCIPTokenPoolChainConfigNotFoundError, CCIPTransactionNotFoundError, CCIPVersionFeatureUnavailableError, CCIPVersionRequiresLaneError, CCIPVersionUnsupportedError, CCIPWalletInvalidError, } from "../errors/index.js";
|
|
7
5
|
import { supportedChains } from "../supported-chains.js";
|
|
8
6
|
import { CCIPVersion, ChainFamily, NetworkType, } from "../types.js";
|
|
9
7
|
import { decodeAddress, decodeOnRampAddress, getAddressBytes, getDataBytes, networkInfo, parseTypeAndVersion, } from "../utils.js";
|
|
10
8
|
import EVM2EVMOffRamp_1_2_ABI from "./abi/OffRamp_1_2.js";
|
|
11
9
|
import EVM2EVMOffRamp_1_5_ABI from "./abi/OffRamp_1_5.js";
|
|
12
10
|
import OffRamp_1_6_ABI from "./abi/OffRamp_1_6.js";
|
|
11
|
+
import OffRamp_2_0_ABI from "./abi/OffRamp_2_0.js";
|
|
13
12
|
import EVM2EVMOnRamp_1_2_ABI from "./abi/OnRamp_1_2.js";
|
|
14
13
|
import EVM2EVMOnRamp_1_5_ABI from "./abi/OnRamp_1_5.js";
|
|
15
|
-
import
|
|
16
|
-
import { DEFAULT_GAS_LIMIT, commitsFragments, defaultAbiCoder, interfaces, receiptsFragments, requestsFragments, } from "./const.js";
|
|
14
|
+
import { CCV_INDEXER_URL, VersionedContractABI, commitsFragments, interfaces, receiptsFragments, requestsFragments, } from "./const.js";
|
|
17
15
|
import { parseData } from "./errors.js";
|
|
16
|
+
import { decodeExtraArgs as decodeExtraArgs_, encodeExtraArgs as encodeExtraArgs_, } from "./extra-args.js";
|
|
18
17
|
import { estimateExecGas } from "./gas.js";
|
|
19
18
|
import { getV12LeafHasher, getV16LeafHasher } from "./hasher.js";
|
|
20
19
|
import { getEvmLogs } from "./logs.js";
|
|
21
|
-
import {
|
|
20
|
+
import { decodeMessageV1, } from "./messages.js";
|
|
21
|
+
export { decodeMessageV1 };
|
|
22
22
|
import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from "./offchain.js";
|
|
23
|
-
import { buildMessageForDest, getMessagesInBatch } from "../requests.js";
|
|
24
|
-
|
|
25
|
-
const EVMExtraArgsV1 = 'tuple(uint256 gasLimit)';
|
|
26
|
-
const EVMExtraArgsV2 = 'tuple(uint256 gasLimit, bool allowOutOfOrderExecution)';
|
|
27
|
-
const SVMExtraArgsV1 = 'tuple(uint32 computeUnits, uint64 accountIsWritableBitmap, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] accounts)';
|
|
28
|
-
const SuiExtraArgsV1 = 'tuple(uint256 gasLimit, bool allowOutOfOrderExecution, bytes32 tokenReceiver, bytes32[] receiverObjectIds)';
|
|
29
|
-
function resultToObject(o) {
|
|
30
|
-
if (o instanceof Promise)
|
|
31
|
-
return o.then(resultToObject);
|
|
32
|
-
if (!(o instanceof Result))
|
|
33
|
-
return o;
|
|
34
|
-
if (o.length === 0)
|
|
35
|
-
return o.toArray();
|
|
36
|
-
try {
|
|
37
|
-
const obj = o.toObject();
|
|
38
|
-
if (!Object.keys(obj).every((k) => /^_+\d*$/.test(k)))
|
|
39
|
-
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, resultToObject(v)]));
|
|
40
|
-
}
|
|
41
|
-
catch (_) {
|
|
42
|
-
// fallthrough
|
|
43
|
-
}
|
|
44
|
-
return o.toArray().map(resultToObject);
|
|
45
|
-
}
|
|
23
|
+
import { buildMessageForDest, decodeMessage, getMessagesInBatch } from "../requests.js";
|
|
24
|
+
import { resultToObject } from "./types.js";
|
|
46
25
|
/** typeguard for ethers Signer interface (used for `wallet`s) */
|
|
47
26
|
function isSigner(wallet) {
|
|
48
27
|
return (typeof wallet === 'object' &&
|
|
@@ -66,6 +45,26 @@ async function submitTransaction(wallet, tx, provider) {
|
|
|
66
45
|
}
|
|
67
46
|
/**
|
|
68
47
|
* EVM chain implementation supporting Ethereum-compatible networks.
|
|
48
|
+
*
|
|
49
|
+
* Provides methods for sending CCIP cross-chain messages, querying message
|
|
50
|
+
* status, fetching fee quotes, and manually executing pending messages on
|
|
51
|
+
* Ethereum Virtual Machine compatible chains.
|
|
52
|
+
*
|
|
53
|
+
* @example Create from RPC URL
|
|
54
|
+
* ```typescript
|
|
55
|
+
* import { EVMChain } from '@chainlink/ccip-sdk'
|
|
56
|
+
*
|
|
57
|
+
* const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
|
|
58
|
+
* console.log(`Connected to: ${chain.network.name}`)
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Query messages in a transaction
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const requests = await chain.getMessagesInTx('0xabc123...')
|
|
64
|
+
* for (const req of requests) {
|
|
65
|
+
* console.log(`Message ID: ${req.message.messageId}`)
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
69
68
|
*/
|
|
70
69
|
export class EVMChain extends Chain {
|
|
71
70
|
static {
|
|
@@ -75,6 +74,13 @@ export class EVMChain extends Chain {
|
|
|
75
74
|
static decimals = 18;
|
|
76
75
|
provider;
|
|
77
76
|
destroy$;
|
|
77
|
+
noncesPromises;
|
|
78
|
+
/**
|
|
79
|
+
* Cache of current nonces per wallet address.
|
|
80
|
+
* Used internally by {@link sendMessage} and {@link execute} to manage transaction ordering.
|
|
81
|
+
* Can be inspected for debugging or manually adjusted if needed.
|
|
82
|
+
*/
|
|
83
|
+
nonces;
|
|
78
84
|
/**
|
|
79
85
|
* Creates a new EVMChain instance.
|
|
80
86
|
* @param provider - JSON-RPC provider for the EVM network.
|
|
@@ -82,6 +88,8 @@ export class EVMChain extends Chain {
|
|
|
82
88
|
*/
|
|
83
89
|
constructor(provider, network, ctx) {
|
|
84
90
|
super(network, ctx);
|
|
91
|
+
this.noncesPromises = {};
|
|
92
|
+
this.nonces = {};
|
|
85
93
|
this.provider = provider;
|
|
86
94
|
this.destroy$ = new Promise((resolve) => (this.destroy = resolve));
|
|
87
95
|
void this.destroy$.finally(() => provider.destroy());
|
|
@@ -116,6 +124,21 @@ export class EVMChain extends Chain {
|
|
|
116
124
|
async listAccounts() {
|
|
117
125
|
return (await this.provider.listAccounts()).map(({ address }) => address);
|
|
118
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the next nonce for a wallet address and increment the internal counter.
|
|
129
|
+
* Fetches from the network on first call, then uses cached value.
|
|
130
|
+
* @param address - Wallet address to get nonce for
|
|
131
|
+
* @returns The next available nonce
|
|
132
|
+
*/
|
|
133
|
+
async nextNonce(address) {
|
|
134
|
+
await (this.noncesPromises[address] ??= this.provider
|
|
135
|
+
.getTransactionCount(address)
|
|
136
|
+
.then((nonce) => {
|
|
137
|
+
this.nonces[address] = nonce;
|
|
138
|
+
return nonce;
|
|
139
|
+
}));
|
|
140
|
+
return this.nonces[address]++;
|
|
141
|
+
}
|
|
119
142
|
/**
|
|
120
143
|
* Creates a JSON-RPC provider from a URL.
|
|
121
144
|
* @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
|
|
@@ -161,9 +184,20 @@ export class EVMChain extends Chain {
|
|
|
161
184
|
}
|
|
162
185
|
/**
|
|
163
186
|
* Creates an EVMChain instance from an RPC URL.
|
|
187
|
+
*
|
|
164
188
|
* @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
|
|
165
|
-
* @param ctx - context containing logger.
|
|
166
|
-
* @returns A new EVMChain instance.
|
|
189
|
+
* @param ctx - Optional context containing logger and API client configuration.
|
|
190
|
+
* @returns A new EVMChain instance connected to the specified network.
|
|
191
|
+
* @throws {@link CCIPChainNotFoundError} if chain cannot be identified from chainId
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* // HTTP connection
|
|
196
|
+
* const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
|
|
197
|
+
*
|
|
198
|
+
* // With custom logger
|
|
199
|
+
* const chain = await EVMChain.fromUrl(url, { logger: customLogger })
|
|
200
|
+
* ```
|
|
167
201
|
*/
|
|
168
202
|
static async fromUrl(url, ctx) {
|
|
169
203
|
return this.fromProvider(await this._getProvider(url), ctx);
|
|
@@ -197,7 +231,7 @@ export class EVMChain extends Chain {
|
|
|
197
231
|
yield* getEvmLogs(filter, this);
|
|
198
232
|
}
|
|
199
233
|
/** {@inheritDoc Chain.getMessagesInBatch} */
|
|
200
|
-
getMessagesInBatch(request,
|
|
234
|
+
getMessagesInBatch(request, range, opts) {
|
|
201
235
|
let opts_;
|
|
202
236
|
if (request.lane.version >= CCIPVersion.V1_6) {
|
|
203
237
|
// specialized getLogs filter for v1.6 CCIPMessageSent events, to filter by dest
|
|
@@ -206,17 +240,22 @@ export class EVMChain extends Chain {
|
|
|
206
240
|
topics: [[request.log.topics[0]], [toBeHex(request.lane.destChainSelector, 32)]],
|
|
207
241
|
};
|
|
208
242
|
}
|
|
209
|
-
return getMessagesInBatch(this, request,
|
|
243
|
+
return getMessagesInBatch(this, request, range, opts_);
|
|
210
244
|
}
|
|
211
245
|
/** {@inheritDoc Chain.typeAndVersion} */
|
|
212
246
|
async typeAndVersion(address) {
|
|
213
247
|
const contract = new Contract(address, VersionedContractABI, this.provider);
|
|
214
|
-
|
|
248
|
+
const res = parseTypeAndVersion(await contract.typeAndVersion());
|
|
249
|
+
if (res[1].startsWith('1.7.'))
|
|
250
|
+
res[1] = CCIPVersion.V2_0;
|
|
251
|
+
return res;
|
|
215
252
|
}
|
|
216
253
|
/**
|
|
217
254
|
* Decodes a CCIP message from a log event.
|
|
218
255
|
* @param log - Log event with topics and data.
|
|
219
256
|
* @returns Decoded CCIPMessage or undefined if not a valid CCIP message.
|
|
257
|
+
* @throws {@link CCIPLogDataInvalidError} if log data is not valid bytes
|
|
258
|
+
* @throws {@link CCIPMessageDecodeError} if message cannot be decoded
|
|
220
259
|
*/
|
|
221
260
|
static decodeMessage(log) {
|
|
222
261
|
if (!isBytesLike(log.data))
|
|
@@ -239,6 +278,9 @@ export class EVMChain extends Chain {
|
|
|
239
278
|
message = resultToObject(result);
|
|
240
279
|
if (message.message)
|
|
241
280
|
message = message.message;
|
|
281
|
+
else if (message.encodedMessage) {
|
|
282
|
+
Object.assign(message, decodeMessageV1(message.encodedMessage));
|
|
283
|
+
}
|
|
242
284
|
if (message)
|
|
243
285
|
break;
|
|
244
286
|
}
|
|
@@ -248,69 +290,15 @@ export class EVMChain extends Chain {
|
|
|
248
290
|
}
|
|
249
291
|
if (!message)
|
|
250
292
|
return;
|
|
251
|
-
|
|
252
|
-
throw new CCIPMessageDecodeError('invalid sender');
|
|
253
|
-
if (message.header) {
|
|
254
|
-
// CCIPMessage_V1_6
|
|
255
|
-
Object.assign(message, message.header);
|
|
256
|
-
delete message.header;
|
|
257
|
-
}
|
|
258
|
-
const sourceFamily = networkInfo(message.sourceChainSelector).family;
|
|
259
|
-
let destFamily = ChainFamily.EVM;
|
|
260
|
-
if (message.destChainSelector) {
|
|
261
|
-
destFamily = networkInfo(message.destChainSelector).family;
|
|
262
|
-
}
|
|
263
|
-
// conversions to make any message version be compatible with latest v1.6
|
|
264
|
-
message.tokenAmounts = message.tokenAmounts.map((tokenAmount, i) => {
|
|
265
|
-
if ('sourceTokenData' in message) {
|
|
266
|
-
// CCIPMessage_V1_2_EVM
|
|
267
|
-
try {
|
|
268
|
-
tokenAmount = {
|
|
269
|
-
...parseSourceTokenData(message.sourceTokenData[i]),
|
|
270
|
-
...tokenAmount,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
catch (_) {
|
|
274
|
-
// legacy sourceTokenData
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
|
|
278
|
-
// CCIPMessage_V1_6_EVM
|
|
279
|
-
tokenAmount.destGasAmount = toBigInt(getDataBytes(tokenAmount.destExecData));
|
|
280
|
-
}
|
|
281
|
-
// Can be undefined if the message is from before v1.5 and failed to parse sourceTokenData
|
|
282
|
-
if (tokenAmount.sourcePoolAddress) {
|
|
283
|
-
tokenAmount.sourcePoolAddress = decodeAddress(tokenAmount.sourcePoolAddress, sourceFamily);
|
|
284
|
-
}
|
|
285
|
-
if (tokenAmount.destTokenAddress) {
|
|
286
|
-
tokenAmount.destTokenAddress = decodeAddress(tokenAmount.destTokenAddress, destFamily);
|
|
287
|
-
}
|
|
288
|
-
return tokenAmount;
|
|
289
|
-
});
|
|
290
|
-
message.sender = decodeAddress(message.sender, sourceFamily);
|
|
291
|
-
message.feeToken = decodeAddress(message.feeToken, sourceFamily);
|
|
292
|
-
message.receiver = decodeAddress(message.receiver, destFamily);
|
|
293
|
-
if (message.extraArgs) {
|
|
294
|
-
// v1.6+
|
|
295
|
-
const parsed = this.decodeExtraArgs(message.extraArgs);
|
|
296
|
-
if (!parsed)
|
|
297
|
-
throw new CCIPExtraArgsParseError(message.extraArgs);
|
|
298
|
-
const { _tag, ...rest } = parsed;
|
|
299
|
-
// merge parsed extraArgs to any family in message root object
|
|
300
|
-
Object.assign(message, rest);
|
|
301
|
-
}
|
|
302
|
-
else if (message.nonce === 0n) {
|
|
303
|
-
// v1.2..v1.5 targets EVM only; extraArgs is not explicit, gasLimit is already in
|
|
304
|
-
// message body, allowOutOfOrderExecution (in v1.5) was present only as nonce=0
|
|
305
|
-
message.allowOutOfOrderExecution = true;
|
|
306
|
-
}
|
|
307
|
-
return message;
|
|
293
|
+
return decodeMessage(message);
|
|
308
294
|
}
|
|
309
295
|
/**
|
|
310
296
|
* Decodes commit reports from a log event.
|
|
311
297
|
* @param log - Log event with topics and data.
|
|
312
298
|
* @param lane - Lane info (required for CCIP v1.5 and earlier).
|
|
313
299
|
* @returns Array of CommitReport or undefined if not a valid commit event.
|
|
300
|
+
* @throws {@link CCIPLogDataInvalidError} if log data is not valid bytes
|
|
301
|
+
* @throws {@link CCIPVersionRequiresLaneError} if CCIP v1.5 event but no lane provided
|
|
314
302
|
*/
|
|
315
303
|
static decodeCommits(log, lane) {
|
|
316
304
|
if (!isBytesLike(log.data))
|
|
@@ -370,6 +358,7 @@ export class EVMChain extends Chain {
|
|
|
370
358
|
* Decodes an execution receipt from a log event.
|
|
371
359
|
* @param log - Log event with topics and data.
|
|
372
360
|
* @returns ExecutionReceipt or undefined if not a valid execution event.
|
|
361
|
+
* @throws {@link CCIPLogDataInvalidError} if log data is not valid bytes
|
|
373
362
|
*/
|
|
374
363
|
static decodeReceipt(log) {
|
|
375
364
|
if (!isBytesLike(log.data))
|
|
@@ -403,34 +392,7 @@ export class EVMChain extends Chain {
|
|
|
403
392
|
* @returns Decoded extra arguments with tag, or undefined if unknown format.
|
|
404
393
|
*/
|
|
405
394
|
static decodeExtraArgs(extraArgs) {
|
|
406
|
-
|
|
407
|
-
switch (tag) {
|
|
408
|
-
case EVMExtraArgsV1Tag: {
|
|
409
|
-
const args = defaultAbiCoder.decode([EVMExtraArgsV1], dataSlice(data, 4));
|
|
410
|
-
return { ...resultToObject(args[0]), _tag: 'EVMExtraArgsV1' };
|
|
411
|
-
}
|
|
412
|
-
case EVMExtraArgsV2Tag: {
|
|
413
|
-
const args = defaultAbiCoder.decode([EVMExtraArgsV2], dataSlice(data, 4));
|
|
414
|
-
return { ...resultToObject(args[0]), _tag: 'EVMExtraArgsV2' };
|
|
415
|
-
}
|
|
416
|
-
case SVMExtraArgsV1Tag: {
|
|
417
|
-
const args = defaultAbiCoder.decode([SVMExtraArgsV1], dataSlice(data, 4));
|
|
418
|
-
const parsed = resultToObject(args[0]);
|
|
419
|
-
parsed.tokenReceiver = encodeBase58(parsed.tokenReceiver);
|
|
420
|
-
parsed.accounts = parsed.accounts.map((a) => encodeBase58(a));
|
|
421
|
-
return { ...parsed, _tag: 'SVMExtraArgsV1' };
|
|
422
|
-
}
|
|
423
|
-
case SuiExtraArgsV1Tag: {
|
|
424
|
-
const args = defaultAbiCoder.decode([SuiExtraArgsV1], dataSlice(data, 4));
|
|
425
|
-
const parsed = resultToObject(args[0]);
|
|
426
|
-
return {
|
|
427
|
-
...parsed,
|
|
428
|
-
_tag: 'SuiExtraArgsV1',
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
default:
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
395
|
+
return decodeExtraArgs_(extraArgs);
|
|
434
396
|
}
|
|
435
397
|
/**
|
|
436
398
|
* Encodes extra arguments for a CCIP message.
|
|
@@ -438,46 +400,13 @@ export class EVMChain extends Chain {
|
|
|
438
400
|
* @returns Encoded extra arguments as hex string.
|
|
439
401
|
*/
|
|
440
402
|
static encodeExtraArgs(args) {
|
|
441
|
-
|
|
442
|
-
return '0x';
|
|
443
|
-
if ('computeUnits' in args) {
|
|
444
|
-
return concat([
|
|
445
|
-
SVMExtraArgsV1Tag,
|
|
446
|
-
defaultAbiCoder.encode([SVMExtraArgsV1], [
|
|
447
|
-
{
|
|
448
|
-
...args,
|
|
449
|
-
tokenReceiver: getAddressBytes(args.tokenReceiver),
|
|
450
|
-
accounts: args.accounts.map((a) => getAddressBytes(a)),
|
|
451
|
-
},
|
|
452
|
-
]),
|
|
453
|
-
]);
|
|
454
|
-
}
|
|
455
|
-
else if ('receiverObjectIds' in args) {
|
|
456
|
-
return concat([
|
|
457
|
-
SuiExtraArgsV1Tag,
|
|
458
|
-
defaultAbiCoder.encode([SuiExtraArgsV1], [
|
|
459
|
-
{
|
|
460
|
-
...args,
|
|
461
|
-
tokenReceiver: zeroPadValue(getAddressBytes(args.tokenReceiver), 32),
|
|
462
|
-
receiverObjectIds: args.receiverObjectIds.map((a) => getDataBytes(a)),
|
|
463
|
-
},
|
|
464
|
-
]),
|
|
465
|
-
]);
|
|
466
|
-
}
|
|
467
|
-
else if ('allowOutOfOrderExecution' in args) {
|
|
468
|
-
if (args.gasLimit == null)
|
|
469
|
-
args.gasLimit = DEFAULT_GAS_LIMIT;
|
|
470
|
-
return concat([EVMExtraArgsV2Tag, defaultAbiCoder.encode([EVMExtraArgsV2], [args])]);
|
|
471
|
-
}
|
|
472
|
-
else if (args.gasLimit != null) {
|
|
473
|
-
return concat([EVMExtraArgsV1Tag, defaultAbiCoder.encode([EVMExtraArgsV1], [args])]);
|
|
474
|
-
}
|
|
475
|
-
return '0x';
|
|
403
|
+
return encodeExtraArgs_(args);
|
|
476
404
|
}
|
|
477
405
|
/**
|
|
478
406
|
* Converts bytes to a checksummed EVM address.
|
|
479
407
|
* @param bytes - Bytes to convert (must be 20 bytes or 32 bytes with leading zeros).
|
|
480
408
|
* @returns Checksummed EVM address.
|
|
409
|
+
* @throws {@link CCIPAddressInvalidEvmError} if bytes cannot be converted to a valid EVM address
|
|
481
410
|
*/
|
|
482
411
|
static getAddress(bytes) {
|
|
483
412
|
if (isHexString(bytes, 20))
|
|
@@ -505,6 +434,7 @@ export class EVMChain extends Chain {
|
|
|
505
434
|
* Gets lane configuration from an OnRamp contract.
|
|
506
435
|
* @param onRamp - OnRamp contract address.
|
|
507
436
|
* @returns Lane configuration.
|
|
437
|
+
* @throws {@link CCIPContractTypeInvalidError} if contract doesn't have destChainSelector
|
|
508
438
|
*/
|
|
509
439
|
async getLaneForOnRamp(onRamp) {
|
|
510
440
|
const [, version] = await this.typeAndVersion(onRamp);
|
|
@@ -521,7 +451,10 @@ export class EVMChain extends Chain {
|
|
|
521
451
|
onRamp,
|
|
522
452
|
};
|
|
523
453
|
}
|
|
524
|
-
/**
|
|
454
|
+
/**
|
|
455
|
+
* {@inheritDoc Chain.getRouterForOnRamp}
|
|
456
|
+
* @throws {@link CCIPVersionUnsupportedError} if OnRamp version is not supported
|
|
457
|
+
*/
|
|
525
458
|
async getRouterForOnRamp(onRamp, destChainSelector) {
|
|
526
459
|
const [, version] = await this.typeAndVersion(onRamp);
|
|
527
460
|
let onRampABI;
|
|
@@ -536,16 +469,23 @@ export class EVMChain extends Chain {
|
|
|
536
469
|
return router;
|
|
537
470
|
}
|
|
538
471
|
case CCIPVersion.V1_6: {
|
|
539
|
-
|
|
540
|
-
const contract = new Contract(onRamp, onRampABI, this.provider);
|
|
472
|
+
const contract = new Contract(onRamp, interfaces.OnRamp_v1_6, this.provider);
|
|
541
473
|
const [, , router] = await contract.getDestChainConfig(destChainSelector);
|
|
542
474
|
return router;
|
|
543
475
|
}
|
|
476
|
+
case CCIPVersion.V2_0: {
|
|
477
|
+
const contract = new Contract(onRamp, interfaces.OnRamp_v2_0, this.provider);
|
|
478
|
+
const { router } = await contract.getDestChainConfig(destChainSelector);
|
|
479
|
+
return router;
|
|
480
|
+
}
|
|
544
481
|
default:
|
|
545
482
|
throw new CCIPVersionUnsupportedError(version);
|
|
546
483
|
}
|
|
547
484
|
}
|
|
548
|
-
/**
|
|
485
|
+
/**
|
|
486
|
+
* {@inheritDoc Chain.getRouterForOffRamp}
|
|
487
|
+
* @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
|
|
488
|
+
*/
|
|
549
489
|
async getRouterForOffRamp(offRamp, sourceChainSelector) {
|
|
550
490
|
const [, version] = await this.typeAndVersion(offRamp);
|
|
551
491
|
let offRampABI, router;
|
|
@@ -559,8 +499,11 @@ export class EVMChain extends Chain {
|
|
|
559
499
|
({ router } = await contract.getDynamicConfig());
|
|
560
500
|
break;
|
|
561
501
|
}
|
|
562
|
-
case CCIPVersion.V1_6:
|
|
502
|
+
case CCIPVersion.V1_6:
|
|
563
503
|
offRampABI = OffRamp_1_6_ABI;
|
|
504
|
+
// falls through
|
|
505
|
+
case CCIPVersion.V2_0: {
|
|
506
|
+
offRampABI = OffRamp_2_0_ABI;
|
|
564
507
|
const contract = new Contract(offRamp, offRampABI, this.provider);
|
|
565
508
|
({ router } = await contract.getSourceChainConfig(sourceChainSelector));
|
|
566
509
|
break;
|
|
@@ -588,8 +531,11 @@ export class EVMChain extends Chain {
|
|
|
588
531
|
const contract = new Contract(router, interfaces.Router, this.provider);
|
|
589
532
|
return contract.getOnRamp(destChainSelector);
|
|
590
533
|
}
|
|
591
|
-
/**
|
|
592
|
-
|
|
534
|
+
/**
|
|
535
|
+
* {@inheritDoc Chain.getOnRampsForOffRamp}
|
|
536
|
+
* @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
|
|
537
|
+
*/
|
|
538
|
+
async getOnRampsForOffRamp(offRamp, sourceChainSelector) {
|
|
593
539
|
const [, version] = await this.typeAndVersion(offRamp);
|
|
594
540
|
let offRampABI;
|
|
595
541
|
switch (version) {
|
|
@@ -600,19 +546,42 @@ export class EVMChain extends Chain {
|
|
|
600
546
|
offRampABI ??= EVM2EVMOffRamp_1_5_ABI;
|
|
601
547
|
const contract = new Contract(offRamp, offRampABI, this.provider);
|
|
602
548
|
const { onRamp } = await contract.getStaticConfig();
|
|
603
|
-
return onRamp;
|
|
549
|
+
return [onRamp];
|
|
604
550
|
}
|
|
605
551
|
case CCIPVersion.V1_6: {
|
|
606
552
|
offRampABI = OffRamp_1_6_ABI;
|
|
607
553
|
const contract = new Contract(offRamp, offRampABI, this.provider);
|
|
608
554
|
const { onRamp } = await contract.getSourceChainConfig(sourceChainSelector);
|
|
609
|
-
|
|
555
|
+
if (!onRamp || onRamp.match(/^(0x)?0*$/i))
|
|
556
|
+
return [];
|
|
557
|
+
return [decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)];
|
|
558
|
+
}
|
|
559
|
+
case CCIPVersion.V2_0: {
|
|
560
|
+
offRampABI = OffRamp_2_0_ABI;
|
|
561
|
+
const contract = new Contract(offRamp, offRampABI, this.provider);
|
|
562
|
+
const { onRamps } = await contract.getSourceChainConfig(sourceChainSelector);
|
|
563
|
+
const sourceFamily = networkInfo(sourceChainSelector).family;
|
|
564
|
+
return onRamps.map((onRamp) => decodeOnRampAddress(onRamp, sourceFamily));
|
|
610
565
|
}
|
|
611
566
|
default:
|
|
612
567
|
throw new CCIPVersionUnsupportedError(version);
|
|
613
568
|
}
|
|
614
569
|
}
|
|
615
|
-
/**
|
|
570
|
+
/**
|
|
571
|
+
* Fetch the CommitStore set in OffRamp config (CCIP v1.5 and earlier).
|
|
572
|
+
* For CCIP v1.6 and later, it should return the offRamp address.
|
|
573
|
+
*
|
|
574
|
+
* @param offRamp - OffRamp contract address
|
|
575
|
+
* @returns Promise resolving to CommitStore address
|
|
576
|
+
*
|
|
577
|
+
* @example Get commit store
|
|
578
|
+
* ```typescript
|
|
579
|
+
* const commitStore = await dest.getCommitStoreForOffRamp(offRampAddress)
|
|
580
|
+
* // For v1.6+, commitStore === offRampAddress
|
|
581
|
+
* ```
|
|
582
|
+
* @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
|
|
583
|
+
* @internal
|
|
584
|
+
*/
|
|
616
585
|
async getCommitStoreForOffRamp(offRamp) {
|
|
617
586
|
const [, version] = await this.typeAndVersion(offRamp);
|
|
618
587
|
let offRampABI;
|
|
@@ -626,11 +595,8 @@ export class EVMChain extends Chain {
|
|
|
626
595
|
const { commitStore } = await contract.getStaticConfig();
|
|
627
596
|
return commitStore;
|
|
628
597
|
}
|
|
629
|
-
case CCIPVersion.V1_6: {
|
|
630
|
-
return offRamp;
|
|
631
|
-
}
|
|
632
598
|
default:
|
|
633
|
-
|
|
599
|
+
return offRamp;
|
|
634
600
|
}
|
|
635
601
|
}
|
|
636
602
|
/** {@inheritDoc Chain.getTokenForTokenPool} */
|
|
@@ -662,6 +628,8 @@ export class EVMChain extends Chain {
|
|
|
662
628
|
* @param lane - Lane configuration.
|
|
663
629
|
* @param ctx - Context object containing logger.
|
|
664
630
|
* @returns Leaf hasher function.
|
|
631
|
+
* @throws {@link CCIPSourceChainUnsupportedError} if source chain is not EVM for v1.2/v1.5
|
|
632
|
+
* @throws {@link CCIPHasherVersionUnsupportedError} if lane version is not supported
|
|
665
633
|
*/
|
|
666
634
|
static getDestLeafHasher({ sourceChainSelector, destChainSelector, onRamp, version }, ctx) {
|
|
667
635
|
switch (version) {
|
|
@@ -692,7 +660,10 @@ export class EVMChain extends Chain {
|
|
|
692
660
|
: 'ethereum-mainnet';
|
|
693
661
|
return this.getOnRampForRouter(router, networkInfo(someOtherNetwork).chainSelector);
|
|
694
662
|
}
|
|
695
|
-
/**
|
|
663
|
+
/**
|
|
664
|
+
* {@inheritDoc Chain.getTokenAdminRegistryFor}
|
|
665
|
+
* @throws {@link CCIPContractNotRouterError} if address is not a Router, OnRamp, or OffRamp
|
|
666
|
+
*/
|
|
696
667
|
async getTokenAdminRegistryFor(address) {
|
|
697
668
|
let [type, version, typeAndVersion] = await this.typeAndVersion(address);
|
|
698
669
|
if (type === 'TokenAdminRegistry') {
|
|
@@ -720,6 +691,8 @@ export class EVMChain extends Chain {
|
|
|
720
691
|
* @internal
|
|
721
692
|
* @param address - Router or Ramp contract address.
|
|
722
693
|
* @returns FeeQuoter contract address.
|
|
694
|
+
* @throws {@link CCIPContractNotRouterError} if address is not a Router, OnRamp, or OffRamp
|
|
695
|
+
* @throws {@link CCIPVersionFeatureUnavailableError} if contract version is below v1.6
|
|
723
696
|
*/
|
|
724
697
|
async getFeeQuoterFor(address) {
|
|
725
698
|
let [type, version, typeAndVersion] = await this.typeAndVersion(address);
|
|
@@ -797,7 +770,10 @@ export class EVMChain extends Chain {
|
|
|
797
770
|
transactions: txRequests,
|
|
798
771
|
};
|
|
799
772
|
}
|
|
800
|
-
/**
|
|
773
|
+
/**
|
|
774
|
+
* {@inheritDoc Chain.sendMessage}
|
|
775
|
+
* @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
|
|
776
|
+
*/
|
|
801
777
|
async sendMessage(opts) {
|
|
802
778
|
const wallet = opts.wallet;
|
|
803
779
|
if (!isSigner(wallet))
|
|
@@ -807,84 +783,125 @@ export class EVMChain extends Chain {
|
|
|
807
783
|
const approveTxs = txs.transactions.slice(0, txs.transactions.length - 1);
|
|
808
784
|
let sendTx = txs.transactions[txs.transactions.length - 1];
|
|
809
785
|
// approve all tokens (including feeToken, if needed) in parallel
|
|
810
|
-
let nonce = await this.provider.getTransactionCount(sender);
|
|
811
786
|
const responses = await Promise.all(approveTxs.map(async (tx) => {
|
|
812
|
-
tx.nonce =
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
787
|
+
tx.nonce = await this.nextNonce(sender);
|
|
788
|
+
try {
|
|
789
|
+
tx = await wallet.populateTransaction(tx);
|
|
790
|
+
tx.from = undefined;
|
|
791
|
+
const response = await submitTransaction(wallet, tx, this.provider);
|
|
792
|
+
this.logger.debug('approve =>', response.hash);
|
|
793
|
+
return response;
|
|
794
|
+
}
|
|
795
|
+
catch (err) {
|
|
796
|
+
this.nonces[sender]--;
|
|
797
|
+
throw err;
|
|
798
|
+
}
|
|
818
799
|
}));
|
|
819
800
|
if (responses.length)
|
|
820
801
|
await responses[responses.length - 1].wait(1, 60_000); // wait last tx nonce to be mined
|
|
821
|
-
sendTx.nonce =
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
802
|
+
sendTx.nonce = await this.nextNonce(sender);
|
|
803
|
+
let response;
|
|
804
|
+
try {
|
|
805
|
+
// sendTx.gasLimit = await this.provider.estimateGas(sendTx)
|
|
806
|
+
sendTx = await wallet.populateTransaction(sendTx);
|
|
807
|
+
sendTx.from = undefined; // some signers don't like receiving pre-populated `from`
|
|
808
|
+
response = await submitTransaction(wallet, sendTx, this.provider);
|
|
809
|
+
}
|
|
810
|
+
catch (err) {
|
|
811
|
+
this.nonces[sender]--;
|
|
812
|
+
throw err;
|
|
813
|
+
}
|
|
826
814
|
this.logger.debug('ccipSend =>', response.hash);
|
|
827
|
-
await response.wait(1, 60_000);
|
|
828
|
-
return (await this.getMessagesInTx(await this.getTransaction(
|
|
815
|
+
const tx = (await response.wait(1, 60_000));
|
|
816
|
+
return (await this.getMessagesInTx(await this.getTransaction(tx)))[0];
|
|
829
817
|
}
|
|
830
818
|
/** {@inheritDoc Chain.getOffchainTokenData} */
|
|
831
819
|
getOffchainTokenData(request) {
|
|
832
820
|
return fetchEVMOffchainTokenData(request, this);
|
|
833
821
|
}
|
|
834
822
|
/**
|
|
835
|
-
* {@inheritDoc Chain.
|
|
823
|
+
* {@inheritDoc Chain.generateUnsignedExecute}
|
|
836
824
|
* @returns array containing one unsigned `manuallyExecute` TransactionRequest object
|
|
825
|
+
* @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
|
|
837
826
|
*/
|
|
838
|
-
async
|
|
839
|
-
|
|
827
|
+
async generateUnsignedExecute(opts) {
|
|
828
|
+
let input, offRamp;
|
|
829
|
+
if (!('input' in opts)) {
|
|
830
|
+
if (!this.apiClient)
|
|
831
|
+
throw new CCIPApiClientNotAvailableError();
|
|
832
|
+
({ offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId));
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
;
|
|
836
|
+
({ offRamp, input } = opts);
|
|
837
|
+
}
|
|
838
|
+
if ('verifications' in input) {
|
|
839
|
+
const contract = new Contract(offRamp, interfaces.OffRamp_v2_0, this.provider);
|
|
840
|
+
const message = decodeMessageV1(input.encodedMessage);
|
|
841
|
+
const messageId = keccak256(input.encodedMessage);
|
|
842
|
+
// `execute` doesn't revert on failure, so we need to estimate using `executeSingleMessage`
|
|
843
|
+
const txGasLimit = await contract.executeSingleMessage.estimateGas({
|
|
844
|
+
...message,
|
|
845
|
+
executionGasLimit: BigInt(message.executionGasLimit),
|
|
846
|
+
ccipReceiveGasLimit: BigInt(message.ccipReceiveGasLimit),
|
|
847
|
+
finality: BigInt(message.finality),
|
|
848
|
+
}, messageId, input.verifications.map(({ destAddress }) => destAddress), input.verifications.map(({ ccvData }) => hexlify(ccvData)), BigInt(opts.gasLimit ?? 0), { from: offRamp });
|
|
849
|
+
const execTx = await contract.execute.populateTransaction(input.encodedMessage, input.verifications.map(({ destAddress }) => destAddress), input.verifications.map(({ ccvData }) => hexlify(ccvData)), BigInt(opts.gasLimit ?? 0));
|
|
850
|
+
execTx.gasLimit = txGasLimit + 40000n; // plus `execute`'s overhead
|
|
851
|
+
return { family: ChainFamily.EVM, transactions: [execTx] };
|
|
852
|
+
}
|
|
840
853
|
let manualExecTx;
|
|
841
|
-
const
|
|
854
|
+
const [_, version] = await this.typeAndVersion(offRamp);
|
|
855
|
+
const offchainTokenData = input.offchainTokenData.map(encodeEVMOffchainTokenData);
|
|
842
856
|
switch (version) {
|
|
843
857
|
case CCIPVersion.V1_2: {
|
|
844
|
-
const contract = new Contract(offRamp,
|
|
858
|
+
const contract = new Contract(offRamp, interfaces.EVM2EVMOffRamp_v1_2, this.provider);
|
|
845
859
|
const gasOverride = BigInt(opts.gasLimit ?? 0);
|
|
846
860
|
manualExecTx = await contract.manuallyExecute.populateTransaction({
|
|
847
|
-
...
|
|
848
|
-
proofs:
|
|
849
|
-
messages: [
|
|
861
|
+
...input,
|
|
862
|
+
proofs: input.proofs.map((d) => hexlify(d)),
|
|
863
|
+
messages: [input.message],
|
|
850
864
|
offchainTokenData: [offchainTokenData],
|
|
851
865
|
}, [gasOverride]);
|
|
852
866
|
break;
|
|
853
867
|
}
|
|
854
868
|
case CCIPVersion.V1_5: {
|
|
855
|
-
const contract = new Contract(offRamp,
|
|
869
|
+
const contract = new Contract(offRamp, interfaces.EVM2EVMOffRamp_v1_5, this.provider);
|
|
856
870
|
manualExecTx = await contract.manuallyExecute.populateTransaction({
|
|
857
|
-
...
|
|
858
|
-
proofs:
|
|
859
|
-
messages: [
|
|
871
|
+
...input,
|
|
872
|
+
proofs: input.proofs.map((d) => hexlify(d)),
|
|
873
|
+
messages: [input.message],
|
|
860
874
|
offchainTokenData: [offchainTokenData],
|
|
861
875
|
}, [
|
|
862
876
|
{
|
|
863
877
|
receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
|
|
864
|
-
tokenGasOverrides:
|
|
878
|
+
tokenGasOverrides: input.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
|
|
865
879
|
},
|
|
866
880
|
]);
|
|
867
881
|
break;
|
|
868
882
|
}
|
|
869
883
|
case CCIPVersion.V1_6: {
|
|
870
884
|
// normalize message
|
|
871
|
-
const
|
|
872
|
-
|
|
885
|
+
const senderBytes = getAddressBytes(input.message.sender);
|
|
886
|
+
// Addresses ≤32 bytes (EVM 20B, Aptos/Solana/Sui 32B) are zero-padded to 32 bytes;
|
|
887
|
+
// Addresses >32 bytes (e.g., TON 36B) are used as raw bytes without padding
|
|
888
|
+
const sender = senderBytes.length <= 32 ? zeroPadValue(senderBytes, 32) : hexlify(senderBytes);
|
|
889
|
+
const tokenAmounts = input.message.tokenAmounts.map((ta) => ({
|
|
873
890
|
...ta,
|
|
874
891
|
sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
|
|
875
892
|
extraData: hexlify(getDataBytes(ta.extraData)),
|
|
876
893
|
}));
|
|
877
894
|
const message = {
|
|
878
|
-
...
|
|
895
|
+
...input.message,
|
|
879
896
|
sender,
|
|
880
897
|
tokenAmounts,
|
|
881
898
|
};
|
|
882
|
-
const contract = new Contract(offRamp,
|
|
899
|
+
const contract = new Contract(offRamp, interfaces.OffRamp_v1_6, this.provider);
|
|
883
900
|
manualExecTx = await contract.manuallyExecute.populateTransaction([
|
|
884
901
|
{
|
|
885
|
-
...
|
|
886
|
-
proofs:
|
|
887
|
-
sourceChainSelector:
|
|
902
|
+
...input,
|
|
903
|
+
proofs: input.proofs.map((p) => hexlify(p)),
|
|
904
|
+
sourceChainSelector: input.message.sourceChainSelector,
|
|
888
905
|
messages: [
|
|
889
906
|
{
|
|
890
907
|
...message,
|
|
@@ -903,7 +920,7 @@ export class EVMChain extends Chain {
|
|
|
903
920
|
[
|
|
904
921
|
{
|
|
905
922
|
receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
|
|
906
|
-
tokenGasOverrides:
|
|
923
|
+
tokenGasOverrides: input.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
|
|
907
924
|
},
|
|
908
925
|
],
|
|
909
926
|
]);
|
|
@@ -914,18 +931,25 @@ export class EVMChain extends Chain {
|
|
|
914
931
|
}
|
|
915
932
|
return { family: ChainFamily.EVM, transactions: [manualExecTx] };
|
|
916
933
|
}
|
|
917
|
-
/**
|
|
918
|
-
|
|
934
|
+
/**
|
|
935
|
+
* {@inheritDoc Chain.execute}
|
|
936
|
+
* @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
|
|
937
|
+
* @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
|
|
938
|
+
* @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
|
|
939
|
+
*/
|
|
940
|
+
async execute(opts) {
|
|
919
941
|
const wallet = opts.wallet;
|
|
920
942
|
if (!isSigner(wallet))
|
|
921
943
|
throw new CCIPWalletInvalidError(wallet);
|
|
922
|
-
const unsignedTxs = await this.
|
|
944
|
+
const unsignedTxs = await this.generateUnsignedExecute({
|
|
923
945
|
...opts,
|
|
924
946
|
payer: await wallet.getAddress(),
|
|
925
947
|
});
|
|
926
|
-
const unsignedTx =
|
|
927
|
-
unsignedTx.
|
|
928
|
-
const
|
|
948
|
+
const unsignedTx = unsignedTxs.transactions[0];
|
|
949
|
+
unsignedTx.nonce = await this.nextNonce(await wallet.getAddress());
|
|
950
|
+
const populatedTx = await wallet.populateTransaction(unsignedTx);
|
|
951
|
+
populatedTx.from = undefined; // some signers don't like receiving pre-populated `from`
|
|
952
|
+
const response = await submitTransaction(wallet, populatedTx, this.provider);
|
|
929
953
|
this.logger.debug('manuallyExecute =>', response.hash);
|
|
930
954
|
const receipt = await response.wait(1, 60_000);
|
|
931
955
|
if (!receipt?.hash)
|
|
@@ -960,7 +984,10 @@ export class EVMChain extends Chain {
|
|
|
960
984
|
} while (page.length === limit);
|
|
961
985
|
return res;
|
|
962
986
|
}
|
|
963
|
-
/**
|
|
987
|
+
/**
|
|
988
|
+
* {@inheritDoc Chain.getRegistryTokenConfig}
|
|
989
|
+
* @throws {@link CCIPTokenNotConfiguredError} if token is not configured in registry
|
|
990
|
+
*/
|
|
964
991
|
async getRegistryTokenConfig(registry, token) {
|
|
965
992
|
const contract = new Contract(registry, interfaces.TokenAdminRegistry, this.provider);
|
|
966
993
|
const config = (await resultToObject(contract.getTokenConfig(token)));
|
|
@@ -975,8 +1002,8 @@ export class EVMChain extends Chain {
|
|
|
975
1002
|
administrator: config.administrator,
|
|
976
1003
|
};
|
|
977
1004
|
}
|
|
978
|
-
/** {@inheritDoc Chain.
|
|
979
|
-
async
|
|
1005
|
+
/** {@inheritDoc Chain.getTokenPoolConfig} */
|
|
1006
|
+
async getTokenPoolConfig(tokenPool) {
|
|
980
1007
|
const [_, , typeAndVersion] = await this.typeAndVersion(tokenPool);
|
|
981
1008
|
const contract = new Contract(tokenPool, interfaces.TokenPool_v1_6, this.provider);
|
|
982
1009
|
const token = contract.getToken();
|
|
@@ -1018,17 +1045,25 @@ export class EVMChain extends Chain {
|
|
|
1018
1045
|
resultToObject(contract.getCurrentInboundRateLimiterState(chain.chainSelector)),
|
|
1019
1046
|
resultToObject(contract.getCurrentOutboundRateLimiterState(chain.chainSelector)),
|
|
1020
1047
|
]))));
|
|
1021
|
-
return Promise.all([supportedChains, remotePools, remoteInfo]).then(([supportedChains, remotePools, remoteInfo]) => Object.fromEntries(supportedChains.map((chain, i) =>
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1048
|
+
return Promise.all([supportedChains, remotePools, remoteInfo]).then(([supportedChains, remotePools, remoteInfo]) => Object.fromEntries(supportedChains.map((chain, i) => {
|
|
1049
|
+
const remoteTokenRaw = remoteInfo[i][0];
|
|
1050
|
+
if (!remoteTokenRaw || remoteTokenRaw.match(/^(0x)?0*$/))
|
|
1051
|
+
throw new CCIPTokenPoolChainConfigNotFoundError(tokenPool, tokenPool, chain.name);
|
|
1052
|
+
return [
|
|
1053
|
+
chain.name,
|
|
1054
|
+
{
|
|
1055
|
+
remoteToken: decodeAddress(remoteTokenRaw, chain.family),
|
|
1056
|
+
remotePools: remotePools[i].map((pool) => decodeAddress(pool, chain.family)),
|
|
1057
|
+
inboundRateLimiterState: remoteInfo[i][1].isEnabled ? remoteInfo[i][1] : null,
|
|
1058
|
+
outboundRateLimiterState: remoteInfo[i][2].isEnabled ? remoteInfo[i][2] : null,
|
|
1059
|
+
},
|
|
1060
|
+
];
|
|
1061
|
+
})));
|
|
1030
1062
|
}
|
|
1031
|
-
/**
|
|
1063
|
+
/**
|
|
1064
|
+
* {@inheritDoc Chain.getFeeTokens}
|
|
1065
|
+
* @throws {@link CCIPVersionUnsupportedError} if OnRamp version is not supported
|
|
1066
|
+
*/
|
|
1032
1067
|
async getFeeTokens(router) {
|
|
1033
1068
|
const onRamp = await this._getSomeOnRampFor(router);
|
|
1034
1069
|
const [_, version] = await this.typeAndVersion(onRamp);
|
|
@@ -1065,6 +1100,48 @@ export class EVMChain extends Chain {
|
|
|
1065
1100
|
}
|
|
1066
1101
|
return Object.fromEntries(await Promise.all(tokens.map(async (token) => [token, await this.getTokenInfo(token)])));
|
|
1067
1102
|
}
|
|
1103
|
+
/** {@inheritDoc Chain.getVerifications} */
|
|
1104
|
+
async getVerifications(opts) {
|
|
1105
|
+
const { offRamp, request } = opts;
|
|
1106
|
+
if (request.lane.version >= CCIPVersion.V2_0) {
|
|
1107
|
+
const message = request.message;
|
|
1108
|
+
if (!message.encodedMessage)
|
|
1109
|
+
throw new CCIPNotImplementedError(`CCIPAPIClient getMessageById v2 encodedMessage`);
|
|
1110
|
+
const contract = new Contract(offRamp, interfaces.OffRamp_v2_0, this.provider);
|
|
1111
|
+
const ccvs = await contract.getCCVsForMessage(message.encodedMessage);
|
|
1112
|
+
const [requiredCCVs, optionalCCVs, optionalThreshold] = ccvs.map(resultToObject);
|
|
1113
|
+
const verificationPolicy = {
|
|
1114
|
+
requiredCCVs,
|
|
1115
|
+
optionalCCVs,
|
|
1116
|
+
optionalThreshold: Number(optionalThreshold),
|
|
1117
|
+
};
|
|
1118
|
+
if (this.apiClient) {
|
|
1119
|
+
const apiRes = await this.apiClient.getMessageById(request.message.messageId);
|
|
1120
|
+
if ('verifiers' in apiRes.message) {
|
|
1121
|
+
const verifiers = apiRes.message.verifiers;
|
|
1122
|
+
return {
|
|
1123
|
+
verificationPolicy,
|
|
1124
|
+
verifications: verifiers.items.map((item) => ({
|
|
1125
|
+
destAddress: item.destAddress,
|
|
1126
|
+
sourceAddress: item.sourceAddress,
|
|
1127
|
+
ccvData: item.verification.data,
|
|
1128
|
+
timestamp: new Date(item.verification.timestamp).getTime() / 1e3,
|
|
1129
|
+
})),
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
const url = `${CCV_INDEXER_URL}/v1/verifierresults/${request.message.messageId}`;
|
|
1134
|
+
const res = await fetch(url);
|
|
1135
|
+
const json = await res.json();
|
|
1136
|
+
return json;
|
|
1137
|
+
}
|
|
1138
|
+
else if (request.lane.version < CCIPVersion.V1_6) {
|
|
1139
|
+
// v1.2..v1.5 EVM (only) have separate CommitStore
|
|
1140
|
+
opts.offRamp = await this.getCommitStoreForOffRamp(opts.offRamp);
|
|
1141
|
+
}
|
|
1142
|
+
// fallback <=v1.6
|
|
1143
|
+
return super.getVerifications(opts);
|
|
1144
|
+
}
|
|
1068
1145
|
/** {@inheritDoc Chain.getExecutionReceipts} */
|
|
1069
1146
|
async *getExecutionReceipts(opts) {
|
|
1070
1147
|
const { messageId, sourceChainSelector } = opts;
|
|
@@ -1082,10 +1159,13 @@ export class EVMChain extends Chain {
|
|
|
1082
1159
|
};
|
|
1083
1160
|
}
|
|
1084
1161
|
else /* >= V1.6 */ {
|
|
1162
|
+
const topicHash = version === CCIPVersion.V1_6
|
|
1163
|
+
? interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged').topicHash
|
|
1164
|
+
: interfaces.OffRamp_v2_0.getEvent('ExecutionStateChanged').topicHash;
|
|
1085
1165
|
opts_ = {
|
|
1086
1166
|
...opts,
|
|
1087
1167
|
topics: [
|
|
1088
|
-
|
|
1168
|
+
topicHash,
|
|
1089
1169
|
sourceChainSelector ? toBeHex(sourceChainSelector, 32) : null,
|
|
1090
1170
|
null,
|
|
1091
1171
|
messageId ?? null,
|