@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.
Files changed (217) hide show
  1. package/README.md +2 -2
  2. package/dist/all-chains.d.ts +23 -0
  3. package/dist/all-chains.d.ts.map +1 -0
  4. package/dist/all-chains.js +24 -0
  5. package/dist/all-chains.js.map +1 -0
  6. package/dist/api/index.d.ts +31 -19
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/index.js +46 -25
  9. package/dist/api/index.js.map +1 -1
  10. package/dist/api/types.d.ts +24 -30
  11. package/dist/api/types.d.ts.map +1 -1
  12. package/dist/aptos/exec.d.ts +2 -2
  13. package/dist/aptos/exec.d.ts.map +1 -1
  14. package/dist/aptos/exec.js.map +1 -1
  15. package/dist/aptos/hasher.d.ts.map +1 -1
  16. package/dist/aptos/hasher.js +1 -1
  17. package/dist/aptos/hasher.js.map +1 -1
  18. package/dist/aptos/index.d.ts +43 -15
  19. package/dist/aptos/index.d.ts.map +1 -1
  20. package/dist/aptos/index.js +112 -105
  21. package/dist/aptos/index.js.map +1 -1
  22. package/dist/aptos/types.d.ts +2 -19
  23. package/dist/aptos/types.d.ts.map +1 -1
  24. package/dist/aptos/types.js +0 -11
  25. package/dist/aptos/types.js.map +1 -1
  26. package/dist/chain.d.ts +734 -174
  27. package/dist/chain.d.ts.map +1 -1
  28. package/dist/chain.js +216 -31
  29. package/dist/chain.js.map +1 -1
  30. package/dist/commits.d.ts +4 -6
  31. package/dist/commits.d.ts.map +1 -1
  32. package/dist/commits.js +4 -4
  33. package/dist/commits.js.map +1 -1
  34. package/dist/errors/CCIPError.d.ts +33 -4
  35. package/dist/errors/CCIPError.d.ts.map +1 -1
  36. package/dist/errors/CCIPError.js +33 -4
  37. package/dist/errors/CCIPError.js.map +1 -1
  38. package/dist/errors/codes.d.ts +5 -0
  39. package/dist/errors/codes.d.ts.map +1 -1
  40. package/dist/errors/codes.js +5 -1
  41. package/dist/errors/codes.js.map +1 -1
  42. package/dist/errors/index.d.ts +2 -2
  43. package/dist/errors/index.d.ts.map +1 -1
  44. package/dist/errors/index.js +2 -2
  45. package/dist/errors/index.js.map +1 -1
  46. package/dist/errors/recovery.d.ts.map +1 -1
  47. package/dist/errors/recovery.js +6 -1
  48. package/dist/errors/recovery.js.map +1 -1
  49. package/dist/errors/specialized.d.ts +1702 -121
  50. package/dist/errors/specialized.d.ts.map +1 -1
  51. package/dist/errors/specialized.js +1729 -125
  52. package/dist/errors/specialized.js.map +1 -1
  53. package/dist/errors/utils.d.ts.map +1 -1
  54. package/dist/errors/utils.js +0 -1
  55. package/dist/errors/utils.js.map +1 -1
  56. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  57. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  58. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  59. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  60. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  61. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  62. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  63. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  64. package/dist/evm/const.d.ts +12 -2
  65. package/dist/evm/const.d.ts.map +1 -1
  66. package/dist/evm/const.js +8 -2
  67. package/dist/evm/const.js.map +1 -1
  68. package/dist/evm/errors.d.ts.map +1 -1
  69. package/dist/evm/errors.js +7 -2
  70. package/dist/evm/errors.js.map +1 -1
  71. package/dist/evm/extra-args.d.ts +25 -0
  72. package/dist/evm/extra-args.d.ts.map +1 -0
  73. package/dist/evm/extra-args.js +309 -0
  74. package/dist/evm/extra-args.js.map +1 -0
  75. package/dist/evm/gas.d.ts.map +1 -1
  76. package/dist/evm/gas.js +7 -12
  77. package/dist/evm/gas.js.map +1 -1
  78. package/dist/evm/hasher.d.ts.map +1 -1
  79. package/dist/evm/hasher.js +23 -13
  80. package/dist/evm/hasher.js.map +1 -1
  81. package/dist/evm/index.d.ts +140 -35
  82. package/dist/evm/index.d.ts.map +1 -1
  83. package/dist/evm/index.js +306 -226
  84. package/dist/evm/index.js.map +1 -1
  85. package/dist/evm/messages.d.ts +59 -5
  86. package/dist/evm/messages.d.ts.map +1 -1
  87. package/dist/evm/messages.js +210 -0
  88. package/dist/evm/messages.js.map +1 -1
  89. package/dist/evm/offchain.js.map +1 -1
  90. package/dist/evm/types.d.ts +7 -2
  91. package/dist/evm/types.d.ts.map +1 -1
  92. package/dist/evm/types.js +22 -1
  93. package/dist/evm/types.js.map +1 -1
  94. package/dist/execution.d.ts +62 -22
  95. package/dist/execution.d.ts.map +1 -1
  96. package/dist/execution.js +102 -51
  97. package/dist/execution.js.map +1 -1
  98. package/dist/extra-args.d.ts +113 -4
  99. package/dist/extra-args.d.ts.map +1 -1
  100. package/dist/extra-args.js +38 -3
  101. package/dist/extra-args.js.map +1 -1
  102. package/dist/gas.d.ts +31 -5
  103. package/dist/gas.d.ts.map +1 -1
  104. package/dist/gas.js +43 -9
  105. package/dist/gas.js.map +1 -1
  106. package/dist/index.d.ts +11 -10
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +8 -8
  109. package/dist/index.js.map +1 -1
  110. package/dist/requests.d.ts +101 -22
  111. package/dist/requests.d.ts.map +1 -1
  112. package/dist/requests.js +115 -24
  113. package/dist/requests.js.map +1 -1
  114. package/dist/selectors.d.ts.map +1 -1
  115. package/dist/selectors.js +24 -0
  116. package/dist/selectors.js.map +1 -1
  117. package/dist/shared/bcs-codecs.d.ts +61 -0
  118. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  119. package/dist/shared/bcs-codecs.js +102 -0
  120. package/dist/shared/bcs-codecs.js.map +1 -0
  121. package/dist/shared/constants.d.ts +3 -0
  122. package/dist/shared/constants.d.ts.map +1 -0
  123. package/dist/shared/constants.js +3 -0
  124. package/dist/shared/constants.js.map +1 -0
  125. package/dist/solana/exec.d.ts +2 -2
  126. package/dist/solana/exec.d.ts.map +1 -1
  127. package/dist/solana/exec.js.map +1 -1
  128. package/dist/solana/index.d.ts +148 -30
  129. package/dist/solana/index.d.ts.map +1 -1
  130. package/dist/solana/index.js +137 -44
  131. package/dist/solana/index.js.map +1 -1
  132. package/dist/sui/hasher.d.ts.map +1 -1
  133. package/dist/sui/hasher.js +1 -1
  134. package/dist/sui/hasher.js.map +1 -1
  135. package/dist/sui/index.d.ts +49 -19
  136. package/dist/sui/index.d.ts.map +1 -1
  137. package/dist/sui/index.js +76 -43
  138. package/dist/sui/index.js.map +1 -1
  139. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  140. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  141. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  142. package/dist/sui/manuallyExec/index.d.ts +2 -2
  143. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  144. package/dist/ton/exec.d.ts +2 -2
  145. package/dist/ton/exec.d.ts.map +1 -1
  146. package/dist/ton/exec.js.map +1 -1
  147. package/dist/ton/index.d.ts +66 -27
  148. package/dist/ton/index.d.ts.map +1 -1
  149. package/dist/ton/index.js +172 -47
  150. package/dist/ton/index.js.map +1 -1
  151. package/dist/ton/send.d.ts +52 -0
  152. package/dist/ton/send.d.ts.map +1 -0
  153. package/dist/ton/send.js +166 -0
  154. package/dist/ton/send.js.map +1 -0
  155. package/dist/ton/types.d.ts +2 -2
  156. package/dist/ton/types.d.ts.map +1 -1
  157. package/dist/ton/types.js.map +1 -1
  158. package/dist/types.d.ts +148 -12
  159. package/dist/types.d.ts.map +1 -1
  160. package/dist/types.js +6 -1
  161. package/dist/types.js.map +1 -1
  162. package/dist/utils.d.ts +79 -4
  163. package/dist/utils.d.ts.map +1 -1
  164. package/dist/utils.js +92 -7
  165. package/dist/utils.js.map +1 -1
  166. package/package.json +16 -11
  167. package/src/all-chains.ts +26 -0
  168. package/src/api/index.ts +58 -34
  169. package/src/api/types.ts +24 -31
  170. package/src/aptos/exec.ts +2 -2
  171. package/src/aptos/hasher.ts +1 -1
  172. package/src/aptos/index.ts +127 -129
  173. package/src/aptos/types.ts +2 -15
  174. package/src/chain.ts +837 -191
  175. package/src/commits.ts +9 -9
  176. package/src/errors/CCIPError.ts +33 -4
  177. package/src/errors/codes.ts +5 -1
  178. package/src/errors/index.ts +2 -1
  179. package/src/errors/recovery.ts +9 -1
  180. package/src/errors/specialized.ts +1745 -132
  181. package/src/errors/utils.ts +0 -1
  182. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  183. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  184. package/src/evm/const.ts +10 -3
  185. package/src/evm/errors.ts +6 -2
  186. package/src/evm/extra-args.ts +360 -0
  187. package/src/evm/gas.ts +14 -13
  188. package/src/evm/hasher.ts +30 -18
  189. package/src/evm/index.ts +376 -281
  190. package/src/evm/messages.ts +323 -11
  191. package/src/evm/offchain.ts +2 -2
  192. package/src/evm/types.ts +20 -2
  193. package/src/execution.ts +126 -71
  194. package/src/extra-args.ts +118 -4
  195. package/src/gas.ts +44 -11
  196. package/src/index.ts +14 -11
  197. package/src/requests.ts +128 -24
  198. package/src/selectors.ts +24 -0
  199. package/src/shared/bcs-codecs.ts +132 -0
  200. package/src/shared/constants.ts +2 -0
  201. package/src/solana/exec.ts +4 -4
  202. package/src/solana/index.ts +170 -82
  203. package/src/sui/hasher.ts +1 -1
  204. package/src/sui/index.ts +88 -56
  205. package/src/sui/manuallyExec/encoder.ts +2 -2
  206. package/src/sui/manuallyExec/index.ts +2 -2
  207. package/src/ton/exec.ts +2 -2
  208. package/src/ton/index.ts +220 -58
  209. package/src/ton/send.ts +222 -0
  210. package/src/ton/types.ts +2 -2
  211. package/src/types.ts +173 -30
  212. package/src/utils.ts +91 -7
  213. package/dist/aptos/utils.d.ts +0 -12
  214. package/dist/aptos/utils.d.ts.map +0 -1
  215. package/dist/aptos/utils.js +0 -15
  216. package/dist/aptos/utils.js.map +0 -1
  217. package/src/aptos/utils.ts +0 -24
package/dist/evm/index.js CHANGED
@@ -1,48 +1,27 @@
1
- import { parseAbi } from 'abitype';
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, CCIPExtraArgsParseError, CCIPHasherVersionUnsupportedError, CCIPLogDataInvalidError, CCIPMessageDecodeError, CCIPSourceChainUnsupportedError, CCIPTokenNotConfiguredError, CCIPTransactionNotFoundError, CCIPVersionFeatureUnavailableError, CCIPVersionRequiresLaneError, CCIPVersionUnsupportedError, CCIPWalletInvalidError, } from "../errors/index.js";
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 OnRamp_1_6_ABI from "./abi/OnRamp_1_6.js";
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 { 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
- 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, commit, opts) {
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, commit, opts_);
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
- 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;
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
- if (!isHexString(message.sender, 20))
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
- const data = getDataBytes(extraArgs), tag = dataSlice(data, 0, 4);
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
- if (!args)
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
- /** {@inheritDoc Chain.getRouterForOnRamp} */
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
- onRampABI = OnRamp_1_6_ABI;
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
- /** {@inheritDoc Chain.getRouterForOffRamp} */
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
- /** {@inheritDoc Chain.getOnRampForOffRamp} */
592
- async getOnRampForOffRamp(offRamp, sourceChainSelector) {
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
- 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));
610
565
  }
611
566
  default:
612
567
  throw new CCIPVersionUnsupportedError(version);
613
568
  }
614
569
  }
615
- /** {@inheritDoc Chain.getCommitStoreForOffRamp} */
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
- throw new CCIPVersionUnsupportedError(version);
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
- /** {@inheritDoc Chain.getTokenAdminRegistryFor} */
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
- /** {@inheritDoc Chain.sendMessage} */
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 = nonce++;
813
- tx = await wallet.populateTransaction(tx);
814
- tx.from = undefined;
815
- const response = await submitTransaction(wallet, tx, this.provider);
816
- this.logger.debug('approve =>', response.hash);
817
- 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
+ }
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 = nonce++;
822
- // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
823
- sendTx = await wallet.populateTransaction(sendTx);
824
- sendTx.from = undefined; // some signers don't like receiving pre-populated `from`
825
- 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
+ }
826
814
  this.logger.debug('ccipSend =>', response.hash);
827
- await response.wait(1, 60_000);
828
- 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];
829
817
  }
830
818
  /** {@inheritDoc Chain.getOffchainTokenData} */
831
819
  getOffchainTokenData(request) {
832
820
  return fetchEVMOffchainTokenData(request, this);
833
821
  }
834
822
  /**
835
- * {@inheritDoc Chain.generateUnsignedExecuteReport}
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 generateUnsignedExecuteReport({ offRamp, execReport, ...opts }) {
839
- 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
+ }
840
853
  let manualExecTx;
841
- const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData);
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, EVM2EVMOffRamp_1_2_ABI, this.provider);
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
- ...execReport,
848
- proofs: execReport.proofs.map((d) => hexlify(d)),
849
- messages: [execReport.message],
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, EVM2EVMOffRamp_1_5_ABI, this.provider);
869
+ const contract = new Contract(offRamp, interfaces.EVM2EVMOffRamp_v1_5, this.provider);
856
870
  manualExecTx = await contract.manuallyExecute.populateTransaction({
857
- ...execReport,
858
- proofs: execReport.proofs.map((d) => hexlify(d)),
859
- messages: [execReport.message],
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: execReport.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
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 sender = zeroPadValue(getAddressBytes(execReport.message.sender), 32);
872
- 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) => ({
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
- ...execReport.message,
895
+ ...input.message,
879
896
  sender,
880
897
  tokenAmounts,
881
898
  };
882
- const contract = new Contract(offRamp, OffRamp_1_6_ABI, this.provider);
899
+ const contract = new Contract(offRamp, interfaces.OffRamp_v1_6, this.provider);
883
900
  manualExecTx = await contract.manuallyExecute.populateTransaction([
884
901
  {
885
- ...execReport,
886
- proofs: execReport.proofs.map((p) => hexlify(p)),
887
- sourceChainSelector: execReport.message.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: execReport.message.tokenAmounts.map(() => BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0)),
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
- /** {@inheritDoc Chain.executeReport} */
918
- async executeReport(opts) {
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.generateUnsignedExecuteReport({
944
+ const unsignedTxs = await this.generateUnsignedExecute({
923
945
  ...opts,
924
946
  payer: await wallet.getAddress(),
925
947
  });
926
- const unsignedTx = await wallet.populateTransaction(unsignedTxs.transactions[0]);
927
- unsignedTx.from = undefined; // some signers don't like receiving pre-populated `from`
928
- 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);
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
- /** {@inheritDoc Chain.getRegistryTokenConfig} */
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.getTokenPoolConfigs} */
979
- async getTokenPoolConfigs(tokenPool) {
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
- chain.name,
1023
- {
1024
- remoteToken: decodeAddress(remoteInfo[i][0], chain.family),
1025
- remotePools: remotePools[i].map((pool) => decodeAddress(pool, chain.family)),
1026
- inboundRateLimiterState: remoteInfo[i][1].isEnabled ? remoteInfo[i][1] : null,
1027
- outboundRateLimiterState: remoteInfo[i][2].isEnabled ? remoteInfo[i][2] : null,
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
- /** {@inheritDoc Chain.getFeeTokens} */
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
- interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged').topicHash,
1168
+ topicHash,
1089
1169
  sourceChainSelector ? toBeHex(sourceChainSelector, 32) : null,
1090
1170
  null,
1091
1171
  messageId ?? null,