@chainlink/ccip-sdk 0.96.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.
Files changed (199) hide show
  1. package/dist/api/index.d.ts +17 -8
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +27 -10
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/types.d.ts +0 -2
  6. package/dist/api/types.d.ts.map +1 -1
  7. package/dist/aptos/exec.d.ts +2 -2
  8. package/dist/aptos/exec.d.ts.map +1 -1
  9. package/dist/aptos/exec.js.map +1 -1
  10. package/dist/aptos/hasher.d.ts.map +1 -1
  11. package/dist/aptos/hasher.js +1 -1
  12. package/dist/aptos/hasher.js.map +1 -1
  13. package/dist/aptos/index.d.ts +13 -10
  14. package/dist/aptos/index.d.ts.map +1 -1
  15. package/dist/aptos/index.js +42 -68
  16. package/dist/aptos/index.js.map +1 -1
  17. package/dist/aptos/types.d.ts +2 -19
  18. package/dist/aptos/types.d.ts.map +1 -1
  19. package/dist/aptos/types.js +0 -11
  20. package/dist/aptos/types.js.map +1 -1
  21. package/dist/chain.d.ts +532 -151
  22. package/dist/chain.d.ts.map +1 -1
  23. package/dist/chain.js +113 -18
  24. package/dist/chain.js.map +1 -1
  25. package/dist/commits.d.ts +4 -6
  26. package/dist/commits.d.ts.map +1 -1
  27. package/dist/commits.js +4 -4
  28. package/dist/commits.js.map +1 -1
  29. package/dist/errors/CCIPError.d.ts +33 -4
  30. package/dist/errors/CCIPError.d.ts.map +1 -1
  31. package/dist/errors/CCIPError.js +33 -4
  32. package/dist/errors/CCIPError.js.map +1 -1
  33. package/dist/errors/codes.d.ts +3 -0
  34. package/dist/errors/codes.d.ts.map +1 -1
  35. package/dist/errors/codes.js +3 -1
  36. package/dist/errors/codes.js.map +1 -1
  37. package/dist/errors/index.d.ts +1 -1
  38. package/dist/errors/index.d.ts.map +1 -1
  39. package/dist/errors/index.js +1 -1
  40. package/dist/errors/index.js.map +1 -1
  41. package/dist/errors/recovery.d.ts.map +1 -1
  42. package/dist/errors/recovery.js +4 -1
  43. package/dist/errors/recovery.js.map +1 -1
  44. package/dist/errors/specialized.d.ts +1695 -120
  45. package/dist/errors/specialized.d.ts.map +1 -1
  46. package/dist/errors/specialized.js +1715 -123
  47. package/dist/errors/specialized.js.map +1 -1
  48. package/dist/errors/utils.d.ts.map +1 -1
  49. package/dist/errors/utils.js +0 -1
  50. package/dist/errors/utils.js.map +1 -1
  51. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  52. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  53. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  54. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  55. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  56. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  57. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  58. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  59. package/dist/evm/const.d.ts +12 -2
  60. package/dist/evm/const.d.ts.map +1 -1
  61. package/dist/evm/const.js +8 -2
  62. package/dist/evm/const.js.map +1 -1
  63. package/dist/evm/errors.d.ts.map +1 -1
  64. package/dist/evm/errors.js +7 -2
  65. package/dist/evm/errors.js.map +1 -1
  66. package/dist/evm/extra-args.d.ts.map +1 -1
  67. package/dist/evm/extra-args.js +5 -24
  68. package/dist/evm/extra-args.js.map +1 -1
  69. package/dist/evm/hasher.d.ts.map +1 -1
  70. package/dist/evm/hasher.js +23 -13
  71. package/dist/evm/hasher.js.map +1 -1
  72. package/dist/evm/index.d.ts +73 -14
  73. package/dist/evm/index.d.ts.map +1 -1
  74. package/dist/evm/index.js +240 -141
  75. package/dist/evm/index.js.map +1 -1
  76. package/dist/evm/messages.d.ts +59 -5
  77. package/dist/evm/messages.d.ts.map +1 -1
  78. package/dist/evm/messages.js +210 -0
  79. package/dist/evm/messages.js.map +1 -1
  80. package/dist/evm/offchain.js.map +1 -1
  81. package/dist/evm/types.d.ts +7 -2
  82. package/dist/evm/types.d.ts.map +1 -1
  83. package/dist/evm/types.js +22 -1
  84. package/dist/evm/types.js.map +1 -1
  85. package/dist/execution.d.ts +62 -22
  86. package/dist/execution.d.ts.map +1 -1
  87. package/dist/execution.js +98 -61
  88. package/dist/execution.js.map +1 -1
  89. package/dist/extra-args.d.ts +13 -3
  90. package/dist/extra-args.d.ts.map +1 -1
  91. package/dist/extra-args.js +13 -3
  92. package/dist/extra-args.js.map +1 -1
  93. package/dist/gas.d.ts +25 -2
  94. package/dist/gas.d.ts.map +1 -1
  95. package/dist/gas.js +30 -4
  96. package/dist/gas.js.map +1 -1
  97. package/dist/index.d.ts +1 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/requests.d.ts +85 -14
  100. package/dist/requests.d.ts.map +1 -1
  101. package/dist/requests.js +99 -16
  102. package/dist/requests.js.map +1 -1
  103. package/dist/selectors.d.ts.map +1 -1
  104. package/dist/selectors.js +12 -0
  105. package/dist/selectors.js.map +1 -1
  106. package/dist/shared/bcs-codecs.d.ts +61 -0
  107. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  108. package/dist/shared/bcs-codecs.js +102 -0
  109. package/dist/shared/bcs-codecs.js.map +1 -0
  110. package/dist/shared/constants.d.ts +3 -0
  111. package/dist/shared/constants.d.ts.map +1 -0
  112. package/dist/shared/constants.js +3 -0
  113. package/dist/shared/constants.js.map +1 -0
  114. package/dist/solana/exec.d.ts +2 -2
  115. package/dist/solana/exec.d.ts.map +1 -1
  116. package/dist/solana/exec.js.map +1 -1
  117. package/dist/solana/index.d.ts +80 -17
  118. package/dist/solana/index.d.ts.map +1 -1
  119. package/dist/solana/index.js +67 -30
  120. package/dist/solana/index.js.map +1 -1
  121. package/dist/sui/hasher.d.ts.map +1 -1
  122. package/dist/sui/hasher.js +1 -1
  123. package/dist/sui/hasher.js.map +1 -1
  124. package/dist/sui/index.d.ts +14 -12
  125. package/dist/sui/index.d.ts.map +1 -1
  126. package/dist/sui/index.js +38 -34
  127. package/dist/sui/index.js.map +1 -1
  128. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  129. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  130. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  131. package/dist/sui/manuallyExec/index.d.ts +2 -2
  132. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  133. package/dist/ton/exec.d.ts +2 -2
  134. package/dist/ton/exec.d.ts.map +1 -1
  135. package/dist/ton/exec.js.map +1 -1
  136. package/dist/ton/index.d.ts +9 -16
  137. package/dist/ton/index.d.ts.map +1 -1
  138. package/dist/ton/index.js +26 -31
  139. package/dist/ton/index.js.map +1 -1
  140. package/dist/ton/types.d.ts +2 -2
  141. package/dist/ton/types.d.ts.map +1 -1
  142. package/dist/ton/types.js.map +1 -1
  143. package/dist/types.d.ts +46 -11
  144. package/dist/types.d.ts.map +1 -1
  145. package/dist/types.js +6 -1
  146. package/dist/types.js.map +1 -1
  147. package/dist/utils.d.ts +65 -2
  148. package/dist/utils.d.ts.map +1 -1
  149. package/dist/utils.js +74 -2
  150. package/dist/utils.js.map +1 -1
  151. package/package.json +9 -9
  152. package/src/api/index.ts +33 -10
  153. package/src/api/types.ts +0 -2
  154. package/src/aptos/exec.ts +2 -2
  155. package/src/aptos/hasher.ts +1 -1
  156. package/src/aptos/index.ts +51 -89
  157. package/src/aptos/types.ts +2 -15
  158. package/src/chain.ts +581 -163
  159. package/src/commits.ts +9 -9
  160. package/src/errors/CCIPError.ts +33 -4
  161. package/src/errors/codes.ts +3 -1
  162. package/src/errors/index.ts +1 -0
  163. package/src/errors/recovery.ts +7 -1
  164. package/src/errors/specialized.ts +1726 -130
  165. package/src/errors/utils.ts +0 -1
  166. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  167. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  168. package/src/evm/const.ts +10 -3
  169. package/src/evm/errors.ts +6 -2
  170. package/src/evm/extra-args.ts +4 -21
  171. package/src/evm/hasher.ts +30 -18
  172. package/src/evm/index.ts +310 -166
  173. package/src/evm/messages.ts +323 -11
  174. package/src/evm/offchain.ts +2 -2
  175. package/src/evm/types.ts +20 -2
  176. package/src/execution.ts +125 -86
  177. package/src/extra-args.ts +13 -3
  178. package/src/gas.ts +29 -3
  179. package/src/index.ts +2 -2
  180. package/src/requests.ts +112 -16
  181. package/src/selectors.ts +12 -0
  182. package/src/shared/bcs-codecs.ts +132 -0
  183. package/src/shared/constants.ts +2 -0
  184. package/src/solana/exec.ts +4 -4
  185. package/src/solana/index.ts +100 -68
  186. package/src/sui/hasher.ts +1 -1
  187. package/src/sui/index.ts +50 -47
  188. package/src/sui/manuallyExec/encoder.ts +2 -2
  189. package/src/sui/manuallyExec/index.ts +2 -2
  190. package/src/ton/exec.ts +2 -2
  191. package/src/ton/index.ts +37 -40
  192. package/src/ton/types.ts +2 -2
  193. package/src/types.ts +70 -29
  194. package/src/utils.ts +73 -2
  195. package/dist/aptos/utils.d.ts +0 -12
  196. package/dist/aptos/utils.d.ts.map +0 -1
  197. package/dist/aptos/utils.js +0 -15
  198. package/dist/aptos/utils.js.map +0 -1
  199. package/src/aptos/utils.ts +0 -24
package/dist/evm/index.js CHANGED
@@ -1,44 +1,27 @@
1
- import { parseAbi } from 'abitype';
2
- import { Contract, JsonRpcProvider, Result, WebSocketProvider, ZeroAddress, 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, CCIPExtraArgsParseError, CCIPHasherVersionUnsupportedError, CCIPLogDataInvalidError, CCIPMessageDecodeError, CCIPSourceChainUnsupportedError, CCIPTokenNotConfiguredError, CCIPTokenPoolChainConfigNotFoundError, CCIPTransactionNotFoundError, CCIPVersionFeatureUnavailableError, CCIPVersionRequiresLaneError, CCIPVersionUnsupportedError, CCIPWalletInvalidError, } from "../errors/index.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";
6
5
  import { supportedChains } from "../supported-chains.js";
7
6
  import { CCIPVersion, ChainFamily, NetworkType, } from "../types.js";
8
7
  import { decodeAddress, decodeOnRampAddress, getAddressBytes, getDataBytes, networkInfo, parseTypeAndVersion, } from "../utils.js";
9
8
  import EVM2EVMOffRamp_1_2_ABI from "./abi/OffRamp_1_2.js";
10
9
  import EVM2EVMOffRamp_1_5_ABI from "./abi/OffRamp_1_5.js";
11
10
  import OffRamp_1_6_ABI from "./abi/OffRamp_1_6.js";
11
+ import OffRamp_2_0_ABI from "./abi/OffRamp_2_0.js";
12
12
  import EVM2EVMOnRamp_1_2_ABI from "./abi/OnRamp_1_2.js";
13
13
  import EVM2EVMOnRamp_1_5_ABI from "./abi/OnRamp_1_5.js";
14
- import OnRamp_1_6_ABI from "./abi/OnRamp_1_6.js";
15
- import { commitsFragments, interfaces, receiptsFragments, requestsFragments } from "./const.js";
14
+ import { CCV_INDEXER_URL, VersionedContractABI, commitsFragments, interfaces, receiptsFragments, requestsFragments, } from "./const.js";
16
15
  import { parseData } from "./errors.js";
17
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 { parseSourceTokenData, } from "./messages.js";
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
- const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)']);
25
- function resultToObject(o) {
26
- if (o instanceof Promise)
27
- return o.then(resultToObject);
28
- if (!(o instanceof Result))
29
- return o;
30
- if (o.length === 0)
31
- return o.toArray();
32
- try {
33
- const obj = o.toObject();
34
- if (!Object.keys(obj).every((k) => /^_+\d*$/.test(k)))
35
- return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, resultToObject(v)]));
36
- }
37
- catch (_) {
38
- // fallthrough
39
- }
40
- return o.toArray().map(resultToObject);
41
- }
23
+ import { buildMessageForDest, decodeMessage, getMessagesInBatch } from "../requests.js";
24
+ import { resultToObject } from "./types.js";
42
25
  /** typeguard for ethers Signer interface (used for `wallet`s) */
43
26
  function isSigner(wallet) {
44
27
  return (typeof wallet === 'object' &&
@@ -62,6 +45,26 @@ async function submitTransaction(wallet, tx, provider) {
62
45
  }
63
46
  /**
64
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
+ * ```
65
68
  */
66
69
  export class EVMChain extends Chain {
67
70
  static {
@@ -71,6 +74,13 @@ export class EVMChain extends Chain {
71
74
  static decimals = 18;
72
75
  provider;
73
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;
74
84
  /**
75
85
  * Creates a new EVMChain instance.
76
86
  * @param provider - JSON-RPC provider for the EVM network.
@@ -78,6 +88,8 @@ export class EVMChain extends Chain {
78
88
  */
79
89
  constructor(provider, network, ctx) {
80
90
  super(network, ctx);
91
+ this.noncesPromises = {};
92
+ this.nonces = {};
81
93
  this.provider = provider;
82
94
  this.destroy$ = new Promise((resolve) => (this.destroy = resolve));
83
95
  void this.destroy$.finally(() => provider.destroy());
@@ -112,6 +124,21 @@ export class EVMChain extends Chain {
112
124
  async listAccounts() {
113
125
  return (await this.provider.listAccounts()).map(({ address }) => address);
114
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
+ }
115
142
  /**
116
143
  * Creates a JSON-RPC provider from a URL.
117
144
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
@@ -157,9 +184,20 @@ export class EVMChain extends Chain {
157
184
  }
158
185
  /**
159
186
  * Creates an EVMChain instance from an RPC URL.
187
+ *
160
188
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
161
- * @param ctx - context containing logger.
162
- * @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
+ * ```
163
201
  */
164
202
  static async fromUrl(url, ctx) {
165
203
  return this.fromProvider(await this._getProvider(url), ctx);
@@ -193,7 +231,7 @@ export class EVMChain extends Chain {
193
231
  yield* getEvmLogs(filter, this);
194
232
  }
195
233
  /** {@inheritDoc Chain.getMessagesInBatch} */
196
- getMessagesInBatch(request, commit, opts) {
234
+ getMessagesInBatch(request, range, opts) {
197
235
  let opts_;
198
236
  if (request.lane.version >= CCIPVersion.V1_6) {
199
237
  // specialized getLogs filter for v1.6 CCIPMessageSent events, to filter by dest
@@ -202,12 +240,15 @@ export class EVMChain extends Chain {
202
240
  topics: [[request.log.topics[0]], [toBeHex(request.lane.destChainSelector, 32)]],
203
241
  };
204
242
  }
205
- return getMessagesInBatch(this, request, commit, opts_);
243
+ return getMessagesInBatch(this, request, range, opts_);
206
244
  }
207
245
  /** {@inheritDoc Chain.typeAndVersion} */
208
246
  async typeAndVersion(address) {
209
247
  const contract = new Contract(address, VersionedContractABI, this.provider);
210
- return parseTypeAndVersion(await contract.typeAndVersion());
248
+ const res = parseTypeAndVersion(await contract.typeAndVersion());
249
+ if (res[1].startsWith('1.7.'))
250
+ res[1] = CCIPVersion.V2_0;
251
+ return res;
211
252
  }
212
253
  /**
213
254
  * Decodes a CCIP message from a log event.
@@ -237,6 +278,9 @@ export class EVMChain extends Chain {
237
278
  message = resultToObject(result);
238
279
  if (message.message)
239
280
  message = message.message;
281
+ else if (message.encodedMessage) {
282
+ Object.assign(message, decodeMessageV1(message.encodedMessage));
283
+ }
240
284
  if (message)
241
285
  break;
242
286
  }
@@ -246,63 +290,7 @@ export class EVMChain extends Chain {
246
290
  }
247
291
  if (!message)
248
292
  return;
249
- if (!isHexString(message.sender, 20))
250
- throw new CCIPMessageDecodeError('invalid sender');
251
- if (message.header) {
252
- // CCIPMessage_V1_6
253
- Object.assign(message, message.header);
254
- delete message.header;
255
- }
256
- const sourceFamily = networkInfo(message.sourceChainSelector).family;
257
- let destFamily = ChainFamily.EVM;
258
- if (message.destChainSelector) {
259
- destFamily = networkInfo(message.destChainSelector).family;
260
- }
261
- // conversions to make any message version be compatible with latest v1.6
262
- message.tokenAmounts = message.tokenAmounts.map((tokenAmount, i) => {
263
- if ('sourceTokenData' in message) {
264
- // CCIPMessage_V1_2_EVM
265
- try {
266
- tokenAmount = {
267
- ...parseSourceTokenData(message.sourceTokenData[i]),
268
- ...tokenAmount,
269
- };
270
- }
271
- catch (_) {
272
- // legacy sourceTokenData
273
- }
274
- }
275
- if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
276
- // CCIPMessage_V1_6_EVM
277
- tokenAmount.destGasAmount = toBigInt(getDataBytes(tokenAmount.destExecData));
278
- }
279
- // Can be undefined if the message is from before v1.5 and failed to parse sourceTokenData
280
- if (tokenAmount.sourcePoolAddress) {
281
- tokenAmount.sourcePoolAddress = decodeAddress(tokenAmount.sourcePoolAddress, sourceFamily);
282
- }
283
- if (tokenAmount.destTokenAddress) {
284
- tokenAmount.destTokenAddress = decodeAddress(tokenAmount.destTokenAddress, destFamily);
285
- }
286
- return tokenAmount;
287
- });
288
- message.sender = decodeAddress(message.sender, sourceFamily);
289
- message.feeToken = decodeAddress(message.feeToken, sourceFamily);
290
- message.receiver = decodeAddress(message.receiver, destFamily);
291
- if (message.extraArgs) {
292
- // v1.6+
293
- const parsed = this.decodeExtraArgs(message.extraArgs);
294
- if (!parsed)
295
- throw new CCIPExtraArgsParseError(message.extraArgs);
296
- const { _tag, ...rest } = parsed;
297
- // merge parsed extraArgs to any family in message root object
298
- Object.assign(message, rest);
299
- }
300
- else if (message.nonce === 0n) {
301
- // v1.2..v1.5 targets EVM only; extraArgs is not explicit, gasLimit is already in
302
- // message body, allowOutOfOrderExecution (in v1.5) was present only as nonce=0
303
- message.allowOutOfOrderExecution = true;
304
- }
305
- return message;
293
+ return decodeMessage(message);
306
294
  }
307
295
  /**
308
296
  * Decodes commit reports from a log event.
@@ -481,11 +469,15 @@ export class EVMChain extends Chain {
481
469
  return router;
482
470
  }
483
471
  case CCIPVersion.V1_6: {
484
- onRampABI = OnRamp_1_6_ABI;
485
- const contract = new Contract(onRamp, onRampABI, this.provider);
472
+ const contract = new Contract(onRamp, interfaces.OnRamp_v1_6, this.provider);
486
473
  const [, , router] = await contract.getDestChainConfig(destChainSelector);
487
474
  return router;
488
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
+ }
489
481
  default:
490
482
  throw new CCIPVersionUnsupportedError(version);
491
483
  }
@@ -507,8 +499,11 @@ export class EVMChain extends Chain {
507
499
  ({ router } = await contract.getDynamicConfig());
508
500
  break;
509
501
  }
510
- case CCIPVersion.V1_6: {
502
+ case CCIPVersion.V1_6:
511
503
  offRampABI = OffRamp_1_6_ABI;
504
+ // falls through
505
+ case CCIPVersion.V2_0: {
506
+ offRampABI = OffRamp_2_0_ABI;
512
507
  const contract = new Contract(offRamp, offRampABI, this.provider);
513
508
  ({ router } = await contract.getSourceChainConfig(sourceChainSelector));
514
509
  break;
@@ -537,10 +532,10 @@ export class EVMChain extends Chain {
537
532
  return contract.getOnRamp(destChainSelector);
538
533
  }
539
534
  /**
540
- * {@inheritDoc Chain.getOnRampForOffRamp}
535
+ * {@inheritDoc Chain.getOnRampsForOffRamp}
541
536
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
542
537
  */
543
- async getOnRampForOffRamp(offRamp, sourceChainSelector) {
538
+ async getOnRampsForOffRamp(offRamp, sourceChainSelector) {
544
539
  const [, version] = await this.typeAndVersion(offRamp);
545
540
  let offRampABI;
546
541
  switch (version) {
@@ -551,21 +546,41 @@ export class EVMChain extends Chain {
551
546
  offRampABI ??= EVM2EVMOffRamp_1_5_ABI;
552
547
  const contract = new Contract(offRamp, offRampABI, this.provider);
553
548
  const { onRamp } = await contract.getStaticConfig();
554
- return onRamp;
549
+ return [onRamp];
555
550
  }
556
551
  case CCIPVersion.V1_6: {
557
552
  offRampABI = OffRamp_1_6_ABI;
558
553
  const contract = new Contract(offRamp, offRampABI, this.provider);
559
554
  const { onRamp } = await contract.getSourceChainConfig(sourceChainSelector);
560
- return decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family);
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));
561
565
  }
562
566
  default:
563
567
  throw new CCIPVersionUnsupportedError(version);
564
568
  }
565
569
  }
566
570
  /**
567
- * {@inheritDoc Chain.getCommitStoreForOffRamp}
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
+ * ```
568
582
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
583
+ * @internal
569
584
  */
570
585
  async getCommitStoreForOffRamp(offRamp) {
571
586
  const [, version] = await this.typeAndVersion(offRamp);
@@ -580,11 +595,8 @@ export class EVMChain extends Chain {
580
595
  const { commitStore } = await contract.getStaticConfig();
581
596
  return commitStore;
582
597
  }
583
- case CCIPVersion.V1_6: {
584
- return offRamp;
585
- }
586
598
  default:
587
- throw new CCIPVersionUnsupportedError(version);
599
+ return offRamp;
588
600
  }
589
601
  }
590
602
  /** {@inheritDoc Chain.getTokenForTokenPool} */
@@ -771,85 +783,125 @@ export class EVMChain extends Chain {
771
783
  const approveTxs = txs.transactions.slice(0, txs.transactions.length - 1);
772
784
  let sendTx = txs.transactions[txs.transactions.length - 1];
773
785
  // approve all tokens (including feeToken, if needed) in parallel
774
- let nonce = await this.provider.getTransactionCount(sender);
775
786
  const responses = await Promise.all(approveTxs.map(async (tx) => {
776
- tx.nonce = nonce++;
777
- tx = await wallet.populateTransaction(tx);
778
- tx.from = undefined;
779
- const response = await submitTransaction(wallet, tx, this.provider);
780
- this.logger.debug('approve =>', response.hash);
781
- return response;
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
+ }
782
799
  }));
783
800
  if (responses.length)
784
801
  await responses[responses.length - 1].wait(1, 60_000); // wait last tx nonce to be mined
785
- sendTx.nonce = nonce++;
786
- // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
787
- sendTx = await wallet.populateTransaction(sendTx);
788
- sendTx.from = undefined; // some signers don't like receiving pre-populated `from`
789
- const response = await submitTransaction(wallet, sendTx, this.provider);
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
+ }
790
814
  this.logger.debug('ccipSend =>', response.hash);
791
- await response.wait(1, 60_000);
792
- return (await this.getMessagesInTx(await this.getTransaction(response.hash)))[0];
815
+ const tx = (await response.wait(1, 60_000));
816
+ return (await this.getMessagesInTx(await this.getTransaction(tx)))[0];
793
817
  }
794
818
  /** {@inheritDoc Chain.getOffchainTokenData} */
795
819
  getOffchainTokenData(request) {
796
820
  return fetchEVMOffchainTokenData(request, this);
797
821
  }
798
822
  /**
799
- * {@inheritDoc Chain.generateUnsignedExecuteReport}
823
+ * {@inheritDoc Chain.generateUnsignedExecute}
800
824
  * @returns array containing one unsigned `manuallyExecute` TransactionRequest object
801
825
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
802
826
  */
803
- async generateUnsignedExecuteReport({ offRamp, execReport, ...opts }) {
804
- const [_, version] = await this.typeAndVersion(offRamp);
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
+ }
805
853
  let manualExecTx;
806
- const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData);
854
+ const [_, version] = await this.typeAndVersion(offRamp);
855
+ const offchainTokenData = input.offchainTokenData.map(encodeEVMOffchainTokenData);
807
856
  switch (version) {
808
857
  case CCIPVersion.V1_2: {
809
- const contract = new Contract(offRamp, EVM2EVMOffRamp_1_2_ABI, this.provider);
858
+ const contract = new Contract(offRamp, interfaces.EVM2EVMOffRamp_v1_2, this.provider);
810
859
  const gasOverride = BigInt(opts.gasLimit ?? 0);
811
860
  manualExecTx = await contract.manuallyExecute.populateTransaction({
812
- ...execReport,
813
- proofs: execReport.proofs.map((d) => hexlify(d)),
814
- messages: [execReport.message],
861
+ ...input,
862
+ proofs: input.proofs.map((d) => hexlify(d)),
863
+ messages: [input.message],
815
864
  offchainTokenData: [offchainTokenData],
816
865
  }, [gasOverride]);
817
866
  break;
818
867
  }
819
868
  case CCIPVersion.V1_5: {
820
- const contract = new Contract(offRamp, EVM2EVMOffRamp_1_5_ABI, this.provider);
869
+ const contract = new Contract(offRamp, interfaces.EVM2EVMOffRamp_v1_5, this.provider);
821
870
  manualExecTx = await contract.manuallyExecute.populateTransaction({
822
- ...execReport,
823
- proofs: execReport.proofs.map((d) => hexlify(d)),
824
- messages: [execReport.message],
871
+ ...input,
872
+ proofs: input.proofs.map((d) => hexlify(d)),
873
+ messages: [input.message],
825
874
  offchainTokenData: [offchainTokenData],
826
875
  }, [
827
876
  {
828
877
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
829
- tokenGasOverrides: execReport.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
878
+ tokenGasOverrides: input.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
830
879
  },
831
880
  ]);
832
881
  break;
833
882
  }
834
883
  case CCIPVersion.V1_6: {
835
884
  // normalize message
836
- const sender = zeroPadValue(getAddressBytes(execReport.message.sender), 32);
837
- const tokenAmounts = execReport.message.tokenAmounts.map((ta) => ({
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) => ({
838
890
  ...ta,
839
891
  sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
840
892
  extraData: hexlify(getDataBytes(ta.extraData)),
841
893
  }));
842
894
  const message = {
843
- ...execReport.message,
895
+ ...input.message,
844
896
  sender,
845
897
  tokenAmounts,
846
898
  };
847
- const contract = new Contract(offRamp, OffRamp_1_6_ABI, this.provider);
899
+ const contract = new Contract(offRamp, interfaces.OffRamp_v1_6, this.provider);
848
900
  manualExecTx = await contract.manuallyExecute.populateTransaction([
849
901
  {
850
- ...execReport,
851
- proofs: execReport.proofs.map((p) => hexlify(p)),
852
- sourceChainSelector: execReport.message.sourceChainSelector,
902
+ ...input,
903
+ proofs: input.proofs.map((p) => hexlify(p)),
904
+ sourceChainSelector: input.message.sourceChainSelector,
853
905
  messages: [
854
906
  {
855
907
  ...message,
@@ -868,7 +920,7 @@ export class EVMChain extends Chain {
868
920
  [
869
921
  {
870
922
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
871
- tokenGasOverrides: execReport.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
923
+ tokenGasOverrides: input.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
872
924
  },
873
925
  ],
874
926
  ]);
@@ -880,22 +932,24 @@ export class EVMChain extends Chain {
880
932
  return { family: ChainFamily.EVM, transactions: [manualExecTx] };
881
933
  }
882
934
  /**
883
- * {@inheritDoc Chain.executeReport}
935
+ * {@inheritDoc Chain.execute}
884
936
  * @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
885
937
  * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
886
938
  * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
887
939
  */
888
- async executeReport(opts) {
940
+ async execute(opts) {
889
941
  const wallet = opts.wallet;
890
942
  if (!isSigner(wallet))
891
943
  throw new CCIPWalletInvalidError(wallet);
892
- const unsignedTxs = await this.generateUnsignedExecuteReport({
944
+ const unsignedTxs = await this.generateUnsignedExecute({
893
945
  ...opts,
894
946
  payer: await wallet.getAddress(),
895
947
  });
896
- const unsignedTx = await wallet.populateTransaction(unsignedTxs.transactions[0]);
897
- unsignedTx.from = undefined; // some signers don't like receiving pre-populated `from`
898
- const response = await submitTransaction(wallet, unsignedTx, this.provider);
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);
899
953
  this.logger.debug('manuallyExecute =>', response.hash);
900
954
  const receipt = await response.wait(1, 60_000);
901
955
  if (!receipt?.hash)
@@ -1046,6 +1100,48 @@ export class EVMChain extends Chain {
1046
1100
  }
1047
1101
  return Object.fromEntries(await Promise.all(tokens.map(async (token) => [token, await this.getTokenInfo(token)])));
1048
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
+ }
1049
1145
  /** {@inheritDoc Chain.getExecutionReceipts} */
1050
1146
  async *getExecutionReceipts(opts) {
1051
1147
  const { messageId, sourceChainSelector } = opts;
@@ -1063,10 +1159,13 @@ export class EVMChain extends Chain {
1063
1159
  };
1064
1160
  }
1065
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;
1066
1165
  opts_ = {
1067
1166
  ...opts,
1068
1167
  topics: [
1069
- interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged').topicHash,
1168
+ topicHash,
1070
1169
  sourceChainSelector ? toBeHex(sourceChainSelector, 32) : null,
1071
1170
  null,
1072
1171
  messageId ?? null,