@chainlink/ccip-sdk 0.96.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +12 -9
  2. package/dist/api/index.d.ts +21 -8
  3. package/dist/api/index.d.ts.map +1 -1
  4. package/dist/api/index.js +42 -13
  5. package/dist/api/index.js.map +1 -1
  6. package/dist/api/types.d.ts +0 -2
  7. package/dist/api/types.d.ts.map +1 -1
  8. package/dist/aptos/exec.d.ts +2 -2
  9. package/dist/aptos/exec.d.ts.map +1 -1
  10. package/dist/aptos/exec.js.map +1 -1
  11. package/dist/aptos/hasher.d.ts.map +1 -1
  12. package/dist/aptos/hasher.js +1 -1
  13. package/dist/aptos/hasher.js.map +1 -1
  14. package/dist/aptos/index.d.ts +17 -16
  15. package/dist/aptos/index.d.ts.map +1 -1
  16. package/dist/aptos/index.js +42 -73
  17. package/dist/aptos/index.js.map +1 -1
  18. package/dist/aptos/logs.d.ts +2 -2
  19. package/dist/aptos/logs.d.ts.map +1 -1
  20. package/dist/aptos/types.d.ts +2 -19
  21. package/dist/aptos/types.d.ts.map +1 -1
  22. package/dist/aptos/types.js +0 -11
  23. package/dist/aptos/types.js.map +1 -1
  24. package/dist/chain.d.ts +538 -158
  25. package/dist/chain.d.ts.map +1 -1
  26. package/dist/chain.js +132 -19
  27. package/dist/chain.js.map +1 -1
  28. package/dist/commits.d.ts +4 -6
  29. package/dist/commits.d.ts.map +1 -1
  30. package/dist/commits.js +4 -4
  31. package/dist/commits.js.map +1 -1
  32. package/dist/errors/CCIPError.d.ts +33 -4
  33. package/dist/errors/CCIPError.d.ts.map +1 -1
  34. package/dist/errors/CCIPError.js +33 -4
  35. package/dist/errors/CCIPError.js.map +1 -1
  36. package/dist/errors/codes.d.ts +3 -0
  37. package/dist/errors/codes.d.ts.map +1 -1
  38. package/dist/errors/codes.js +3 -1
  39. package/dist/errors/codes.js.map +1 -1
  40. package/dist/errors/index.d.ts +4 -4
  41. package/dist/errors/index.d.ts.map +1 -1
  42. package/dist/errors/index.js +4 -4
  43. package/dist/errors/index.js.map +1 -1
  44. package/dist/errors/recovery.d.ts.map +1 -1
  45. package/dist/errors/recovery.js +4 -1
  46. package/dist/errors/recovery.js.map +1 -1
  47. package/dist/errors/specialized.d.ts +1695 -120
  48. package/dist/errors/specialized.d.ts.map +1 -1
  49. package/dist/errors/specialized.js +1715 -123
  50. package/dist/errors/specialized.js.map +1 -1
  51. package/dist/errors/utils.d.ts.map +1 -1
  52. package/dist/errors/utils.js +0 -1
  53. package/dist/errors/utils.js.map +1 -1
  54. package/dist/evm/abi/OffRamp_2_0.d.ts +764 -0
  55. package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -0
  56. package/dist/evm/abi/OffRamp_2_0.js +744 -0
  57. package/dist/evm/abi/OffRamp_2_0.js.map +1 -0
  58. package/dist/evm/abi/OnRamp_2_0.d.ts +925 -0
  59. package/dist/evm/abi/OnRamp_2_0.d.ts.map +1 -0
  60. package/dist/evm/abi/OnRamp_2_0.js +992 -0
  61. package/dist/evm/abi/OnRamp_2_0.js.map +1 -0
  62. package/dist/evm/const.d.ts +12 -2
  63. package/dist/evm/const.d.ts.map +1 -1
  64. package/dist/evm/const.js +8 -2
  65. package/dist/evm/const.js.map +1 -1
  66. package/dist/evm/errors.d.ts.map +1 -1
  67. package/dist/evm/errors.js +7 -2
  68. package/dist/evm/errors.js.map +1 -1
  69. package/dist/evm/extra-args.d.ts.map +1 -1
  70. package/dist/evm/extra-args.js +5 -24
  71. package/dist/evm/extra-args.js.map +1 -1
  72. package/dist/evm/hasher.d.ts.map +1 -1
  73. package/dist/evm/hasher.js +23 -13
  74. package/dist/evm/hasher.js.map +1 -1
  75. package/dist/evm/index.d.ts +73 -16
  76. package/dist/evm/index.d.ts.map +1 -1
  77. package/dist/evm/index.js +241 -146
  78. package/dist/evm/index.js.map +1 -1
  79. package/dist/evm/logs.d.ts +1 -1
  80. package/dist/evm/logs.js +1 -1
  81. package/dist/evm/messages.d.ts +59 -5
  82. package/dist/evm/messages.d.ts.map +1 -1
  83. package/dist/evm/messages.js +210 -0
  84. package/dist/evm/messages.js.map +1 -1
  85. package/dist/evm/offchain.d.ts +1 -14
  86. package/dist/evm/offchain.d.ts.map +1 -1
  87. package/dist/evm/offchain.js +1 -133
  88. package/dist/evm/offchain.js.map +1 -1
  89. package/dist/evm/types.d.ts +7 -2
  90. package/dist/evm/types.d.ts.map +1 -1
  91. package/dist/evm/types.js +22 -1
  92. package/dist/evm/types.js.map +1 -1
  93. package/dist/execution.d.ts +62 -22
  94. package/dist/execution.d.ts.map +1 -1
  95. package/dist/execution.js +98 -61
  96. package/dist/execution.js.map +1 -1
  97. package/dist/extra-args.d.ts +13 -3
  98. package/dist/extra-args.d.ts.map +1 -1
  99. package/dist/extra-args.js +13 -3
  100. package/dist/extra-args.js.map +1 -1
  101. package/dist/gas.d.ts +25 -2
  102. package/dist/gas.d.ts.map +1 -1
  103. package/dist/gas.js +30 -4
  104. package/dist/gas.js.map +1 -1
  105. package/dist/index.d.ts +3 -2
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +2 -1
  108. package/dist/index.js.map +1 -1
  109. package/dist/offchain.d.ts +23 -6
  110. package/dist/offchain.d.ts.map +1 -1
  111. package/dist/offchain.js +92 -17
  112. package/dist/offchain.js.map +1 -1
  113. package/dist/requests.d.ts +85 -14
  114. package/dist/requests.d.ts.map +1 -1
  115. package/dist/requests.js +99 -17
  116. package/dist/requests.js.map +1 -1
  117. package/dist/selectors.d.ts.map +1 -1
  118. package/dist/selectors.js +12 -0
  119. package/dist/selectors.js.map +1 -1
  120. package/dist/shared/bcs-codecs.d.ts +61 -0
  121. package/dist/shared/bcs-codecs.d.ts.map +1 -0
  122. package/dist/shared/bcs-codecs.js +102 -0
  123. package/dist/shared/bcs-codecs.js.map +1 -0
  124. package/dist/shared/constants.d.ts +3 -0
  125. package/dist/shared/constants.d.ts.map +1 -0
  126. package/dist/shared/constants.js +3 -0
  127. package/dist/shared/constants.js.map +1 -0
  128. package/dist/solana/exec.d.ts +2 -2
  129. package/dist/solana/exec.d.ts.map +1 -1
  130. package/dist/solana/exec.js.map +1 -1
  131. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts +1 -1
  132. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js +1 -1
  133. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts +1 -1
  134. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js +1 -1
  135. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts +1 -1
  136. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js +1 -1
  137. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts +16 -1
  138. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts.map +1 -1
  139. package/dist/solana/idl/1.6.0/CCIP_COMMON.js +16 -1
  140. package/dist/solana/idl/1.6.0/CCIP_COMMON.js.map +1 -1
  141. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts +1 -1
  142. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js +1 -1
  143. package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts +1 -1
  144. package/dist/solana/idl/1.6.0/CCIP_ROUTER.js +1 -1
  145. package/dist/solana/index.d.ts +85 -24
  146. package/dist/solana/index.d.ts.map +1 -1
  147. package/dist/solana/index.js +69 -37
  148. package/dist/solana/index.js.map +1 -1
  149. package/dist/solana/offchain.d.ts +1 -13
  150. package/dist/solana/offchain.d.ts.map +1 -1
  151. package/dist/solana/offchain.js +1 -66
  152. package/dist/solana/offchain.js.map +1 -1
  153. package/dist/solana/utils.d.ts +4 -4
  154. package/dist/solana/utils.d.ts.map +1 -1
  155. package/dist/solana/utils.js +1 -1
  156. package/dist/solana/utils.js.map +1 -1
  157. package/dist/sui/hasher.d.ts.map +1 -1
  158. package/dist/sui/hasher.js +1 -1
  159. package/dist/sui/hasher.js.map +1 -1
  160. package/dist/sui/index.d.ts +18 -18
  161. package/dist/sui/index.d.ts.map +1 -1
  162. package/dist/sui/index.js +38 -39
  163. package/dist/sui/index.js.map +1 -1
  164. package/dist/sui/manuallyExec/encoder.d.ts +2 -2
  165. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  166. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  167. package/dist/sui/manuallyExec/index.d.ts +2 -2
  168. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  169. package/dist/ton/exec.d.ts +3 -3
  170. package/dist/ton/exec.d.ts.map +1 -1
  171. package/dist/ton/exec.js +1 -1
  172. package/dist/ton/exec.js.map +1 -1
  173. package/dist/ton/index.d.ts +14 -22
  174. package/dist/ton/index.d.ts.map +1 -1
  175. package/dist/ton/index.js +26 -35
  176. package/dist/ton/index.js.map +1 -1
  177. package/dist/ton/types.d.ts +3 -5
  178. package/dist/ton/types.d.ts.map +1 -1
  179. package/dist/ton/types.js.map +1 -1
  180. package/dist/types.d.ts +55 -20
  181. package/dist/types.d.ts.map +1 -1
  182. package/dist/types.js +6 -1
  183. package/dist/types.js.map +1 -1
  184. package/dist/utils.d.ts +65 -2
  185. package/dist/utils.d.ts.map +1 -1
  186. package/dist/utils.js +74 -2
  187. package/dist/utils.js.map +1 -1
  188. package/package.json +14 -10
  189. package/src/api/index.ts +53 -17
  190. package/src/api/types.ts +0 -2
  191. package/src/aptos/exec.ts +2 -2
  192. package/src/aptos/hasher.ts +1 -1
  193. package/src/aptos/index.ts +55 -100
  194. package/src/aptos/logs.ts +2 -2
  195. package/src/aptos/types.ts +2 -15
  196. package/src/chain.ts +594 -171
  197. package/src/commits.ts +9 -9
  198. package/src/errors/CCIPError.ts +33 -4
  199. package/src/errors/codes.ts +3 -1
  200. package/src/errors/index.ts +4 -0
  201. package/src/errors/recovery.ts +7 -1
  202. package/src/errors/specialized.ts +1726 -130
  203. package/src/errors/utils.ts +0 -1
  204. package/src/evm/abi/OffRamp_2_0.ts +743 -0
  205. package/src/evm/abi/OnRamp_2_0.ts +991 -0
  206. package/src/evm/const.ts +10 -3
  207. package/src/evm/errors.ts +6 -2
  208. package/src/evm/extra-args.ts +4 -21
  209. package/src/evm/hasher.ts +30 -18
  210. package/src/evm/index.ts +314 -176
  211. package/src/evm/logs.ts +1 -1
  212. package/src/evm/messages.ts +323 -11
  213. package/src/evm/offchain.ts +2 -191
  214. package/src/evm/types.ts +20 -2
  215. package/src/execution.ts +125 -86
  216. package/src/extra-args.ts +13 -3
  217. package/src/gas.ts +29 -3
  218. package/src/index.ts +10 -3
  219. package/src/offchain.ts +125 -28
  220. package/src/requests.ts +114 -19
  221. package/src/selectors.ts +12 -0
  222. package/src/shared/bcs-codecs.ts +132 -0
  223. package/src/shared/constants.ts +2 -0
  224. package/src/solana/exec.ts +4 -4
  225. package/src/solana/idl/1.6.0/BASE_TOKEN_POOL.ts +2 -2
  226. package/src/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.ts +2 -2
  227. package/src/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts +2 -2
  228. package/src/solana/idl/1.6.0/CCIP_COMMON.ts +32 -2
  229. package/src/solana/idl/1.6.0/CCIP_OFFRAMP.ts +2 -2
  230. package/src/solana/idl/1.6.0/CCIP_ROUTER.ts +2 -2
  231. package/src/solana/index.ts +110 -85
  232. package/src/solana/offchain.ts +3 -100
  233. package/src/solana/utils.ts +8 -5
  234. package/src/sui/hasher.ts +1 -1
  235. package/src/sui/index.ts +55 -59
  236. package/src/sui/manuallyExec/encoder.ts +2 -2
  237. package/src/sui/manuallyExec/index.ts +2 -2
  238. package/src/ton/exec.ts +4 -7
  239. package/src/ton/index.ts +45 -53
  240. package/src/ton/types.ts +4 -7
  241. package/src/types.ts +81 -37
  242. package/src/utils.ts +73 -2
  243. package/dist/aptos/utils.d.ts +0 -12
  244. package/dist/aptos/utils.d.ts.map +0 -1
  245. package/dist/aptos/utils.js +0 -15
  246. package/dist/aptos/utils.js.map +0 -1
  247. package/src/aptos/utils.ts +0 -24
package/src/evm/index.ts CHANGED
@@ -1,24 +1,23 @@
1
- import { parseAbi } from 'abitype'
2
1
  import {
3
2
  type BytesLike,
4
3
  type Interface,
5
4
  type JsonRpcApiProvider,
6
5
  type Log,
6
+ type Result,
7
7
  type Signer,
8
8
  type TransactionReceipt,
9
9
  type TransactionRequest,
10
10
  type TransactionResponse,
11
11
  Contract,
12
12
  JsonRpcProvider,
13
- Result,
14
13
  WebSocketProvider,
15
14
  ZeroAddress,
16
15
  getAddress,
17
16
  hexlify,
18
17
  isBytesLike,
19
18
  isHexString,
19
+ keccak256,
20
20
  toBeHex,
21
- toBigInt,
22
21
  zeroPadValue,
23
22
  } from 'ethers'
24
23
  import type { TypedContract } from 'ethers-abitype'
@@ -34,16 +33,16 @@ import {
34
33
  } from '../chain.ts'
35
34
  import {
36
35
  CCIPAddressInvalidEvmError,
36
+ CCIPApiClientNotAvailableError,
37
37
  CCIPBlockNotFoundError,
38
38
  CCIPContractNotRouterError,
39
39
  CCIPContractTypeInvalidError,
40
40
  CCIPDataFormatUnsupportedError,
41
41
  CCIPExecTxNotConfirmedError,
42
42
  CCIPExecTxRevertedError,
43
- CCIPExtraArgsParseError,
44
43
  CCIPHasherVersionUnsupportedError,
45
44
  CCIPLogDataInvalidError,
46
- CCIPMessageDecodeError,
45
+ CCIPNotImplementedError,
47
46
  CCIPSourceChainUnsupportedError,
48
47
  CCIPTokenNotConfiguredError,
49
48
  CCIPTokenPoolChainConfigNotFoundError,
@@ -60,14 +59,15 @@ import {
60
59
  type CCIPExecution,
61
60
  type CCIPMessage,
62
61
  type CCIPRequest,
62
+ type CCIPVerifications,
63
+ type ChainLog,
63
64
  type ChainTransaction,
64
65
  type CommitReport,
66
+ type ExecutionInput,
65
67
  type ExecutionReceipt,
66
68
  type ExecutionState,
67
69
  type Lane,
68
- type Log_,
69
70
  type NetworkInfo,
70
- type OffchainTokenData,
71
71
  type WithLogger,
72
72
  CCIPVersion,
73
73
  ChainFamily,
@@ -88,12 +88,21 @@ import type TokenPool_ABI from './abi/LockReleaseTokenPool_1_6_1.ts'
88
88
  import EVM2EVMOffRamp_1_2_ABI from './abi/OffRamp_1_2.ts'
89
89
  import EVM2EVMOffRamp_1_5_ABI from './abi/OffRamp_1_5.ts'
90
90
  import OffRamp_1_6_ABI from './abi/OffRamp_1_6.ts'
91
+ import OffRamp_2_0_ABI from './abi/OffRamp_2_0.ts'
91
92
  import EVM2EVMOnRamp_1_2_ABI from './abi/OnRamp_1_2.ts'
92
93
  import EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
93
- import OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
94
+ import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
95
+ import type OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
94
96
  import type Router_ABI from './abi/Router.ts'
95
97
  import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
96
- import { commitsFragments, interfaces, receiptsFragments, requestsFragments } from './const.ts'
98
+ import {
99
+ CCV_INDEXER_URL,
100
+ VersionedContractABI,
101
+ commitsFragments,
102
+ interfaces,
103
+ receiptsFragments,
104
+ requestsFragments,
105
+ } from './const.ts'
97
106
  import { parseData } from './errors.ts'
98
107
  import {
99
108
  decodeExtraArgs as decodeExtraArgs_,
@@ -104,30 +113,19 @@ import { getV12LeafHasher, getV16LeafHasher } from './hasher.ts'
104
113
  import { getEvmLogs } from './logs.ts'
105
114
  import {
106
115
  type CCIPMessage_V1_6_EVM,
116
+ type CCIPMessage_V2_0,
107
117
  type CleanAddressable,
108
- parseSourceTokenData,
118
+ type MessageV1,
119
+ type TokenTransferV1,
120
+ decodeMessageV1,
109
121
  } from './messages.ts'
110
- import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from './offchain.ts'
111
- import { buildMessageForDest, getMessagesInBatch } from '../requests.ts'
112
- import type { UnsignedEVMTx } from './types.ts'
122
+ export { decodeMessageV1 }
123
+ export type { MessageV1, TokenTransferV1 }
124
+ import { encodeEVMOffchainTokenData } from './offchain.ts'
125
+ import { buildMessageForDest, decodeMessage, getMessagesInBatch } from '../requests.ts'
126
+ import { type UnsignedEVMTx, resultToObject } from './types.ts'
113
127
  export type { UnsignedEVMTx }
114
128
 
115
- const VersionedContractABI = parseAbi(['function typeAndVersion() view returns (string)'])
116
-
117
- function resultToObject<T>(o: T): T {
118
- if (o instanceof Promise) return o.then(resultToObject) as T
119
- if (!(o instanceof Result)) return o
120
- if (o.length === 0) return o.toArray() as T
121
- try {
122
- const obj = o.toObject()
123
- if (!Object.keys(obj).every((k) => /^_+\d*$/.test(k)))
124
- return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, resultToObject(v)])) as T
125
- } catch (_) {
126
- // fallthrough
127
- }
128
- return o.toArray().map(resultToObject) as T
129
- }
130
-
131
129
  /** typeguard for ethers Signer interface (used for `wallet`s) */
132
130
  function isSigner(wallet: unknown): wallet is Signer {
133
131
  return (
@@ -158,6 +156,26 @@ async function submitTransaction(
158
156
 
159
157
  /**
160
158
  * EVM chain implementation supporting Ethereum-compatible networks.
159
+ *
160
+ * Provides methods for sending CCIP cross-chain messages, querying message
161
+ * status, fetching fee quotes, and manually executing pending messages on
162
+ * Ethereum Virtual Machine compatible chains.
163
+ *
164
+ * @example Create from RPC URL
165
+ * ```typescript
166
+ * import { EVMChain } from '@chainlink/ccip-sdk'
167
+ *
168
+ * const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
169
+ * console.log(`Connected to: ${chain.network.name}`)
170
+ * ```
171
+ *
172
+ * @example Query messages in a transaction
173
+ * ```typescript
174
+ * const requests = await chain.getMessagesInTx('0xabc123...')
175
+ * for (const req of requests) {
176
+ * console.log(`Message ID: ${req.message.messageId}`)
177
+ * }
178
+ * ```
161
179
  */
162
180
  export class EVMChain extends Chain<typeof ChainFamily.EVM> {
163
181
  static {
@@ -168,6 +186,13 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
168
186
 
169
187
  provider: JsonRpcApiProvider
170
188
  readonly destroy$: Promise<void>
189
+ private noncesPromises: Record<string, Promise<unknown>>
190
+ /**
191
+ * Cache of current nonces per wallet address.
192
+ * Used internally by {@link sendMessage} and {@link execute} to manage transaction ordering.
193
+ * Can be inspected for debugging or manually adjusted if needed.
194
+ */
195
+ nonces: Record<string, number>
171
196
 
172
197
  /**
173
198
  * Creates a new EVMChain instance.
@@ -177,6 +202,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
177
202
  constructor(provider: JsonRpcApiProvider, network: NetworkInfo, ctx?: ChainContext) {
178
203
  super(network, ctx)
179
204
 
205
+ this.noncesPromises = {}
206
+ this.nonces = {}
207
+
180
208
  this.provider = provider
181
209
  this.destroy$ = new Promise<void>((resolve) => (this.destroy = resolve))
182
210
  void this.destroy$.finally(() => provider.destroy())
@@ -216,6 +244,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
216
244
  return (await this.provider.listAccounts()).map(({ address }) => address)
217
245
  }
218
246
 
247
+ /**
248
+ * Get the next nonce for a wallet address and increment the internal counter.
249
+ * Fetches from the network on first call, then uses cached value.
250
+ * @param address - Wallet address to get nonce for
251
+ * @returns The next available nonce
252
+ */
253
+ async nextNonce(address: string): Promise<number> {
254
+ await (this.noncesPromises[address] ??= this.provider
255
+ .getTransactionCount(address)
256
+ .then((nonce) => {
257
+ this.nonces[address] = nonce
258
+ return nonce
259
+ }))
260
+ return this.nonces[address]!++
261
+ }
262
+
219
263
  /**
220
264
  * Creates a JSON-RPC provider from a URL.
221
265
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
@@ -260,9 +304,20 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
260
304
 
261
305
  /**
262
306
  * Creates an EVMChain instance from an RPC URL.
307
+ *
263
308
  * @param url - WebSocket (wss://) or HTTP (https://) endpoint URL.
264
- * @param ctx - context containing logger.
265
- * @returns A new EVMChain instance.
309
+ * @param ctx - Optional context containing logger and API client configuration.
310
+ * @returns A new EVMChain instance connected to the specified network.
311
+ * @throws {@link CCIPChainNotFoundError} if chain cannot be identified from chainId
312
+ *
313
+ * @example
314
+ * ```typescript
315
+ * // HTTP connection
316
+ * const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
317
+ *
318
+ * // With custom logger
319
+ * const chain = await EVMChain.fromUrl(url, { logger: customLogger })
320
+ * ```
266
321
  */
267
322
  static async fromUrl(url: string, ctx?: ChainContext): Promise<EVMChain> {
268
323
  return this.fromProvider(await this._getProvider(url), ctx)
@@ -283,9 +338,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
283
338
  const chainTx = {
284
339
  ...tx,
285
340
  timestamp,
286
- logs: [] as Log_[],
341
+ logs: [] as ChainLog[],
287
342
  }
288
- const logs: Log_[] = tx.logs.map((l) => Object.assign(l, { tx: chainTx }))
343
+ const logs: ChainLog[] = tx.logs.map((l) => Object.assign(l, { tx: chainTx }))
289
344
  chainTx.logs = logs
290
345
  return chainTx
291
346
  }
@@ -298,15 +353,15 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
298
353
  }
299
354
 
300
355
  /** {@inheritDoc Chain.getMessagesInBatch} */
301
- getMessagesInBatch<
356
+ override getMessagesInBatch<
302
357
  R extends PickDeep<
303
358
  CCIPRequest,
304
359
  'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
305
360
  >,
306
361
  >(
307
362
  request: R,
308
- commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
309
- opts?: { page?: number },
363
+ range: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
364
+ opts?: Pick<LogFilter, 'page'>,
310
365
  ): Promise<R['message'][]> {
311
366
  let opts_: Parameters<EVMChain['getLogs']>[0] | undefined
312
367
  if (request.lane.version >= CCIPVersion.V1_6) {
@@ -316,7 +371,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
316
371
  topics: [[request.log.topics[0]!], [toBeHex(request.lane.destChainSelector, 32)]],
317
372
  }
318
373
  }
319
- return getMessagesInBatch(this, request, commit, opts_)
374
+ return getMessagesInBatch(this, request, range, opts_)
320
375
  }
321
376
 
322
377
  /** {@inheritDoc Chain.typeAndVersion} */
@@ -326,7 +381,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
326
381
  VersionedContractABI,
327
382
  this.provider,
328
383
  ) as unknown as TypedContract<typeof VersionedContractABI>
329
- return parseTypeAndVersion(await contract.typeAndVersion())
384
+ const res = parseTypeAndVersion(await contract.typeAndVersion())
385
+ if (res[1].startsWith('1.7.')) res[1] = CCIPVersion.V2_0
386
+ return res
330
387
  }
331
388
 
332
389
  /**
@@ -356,79 +413,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
356
413
  const result = interfaces.OnRamp_v1_6.decodeEventLog(fragment, log.data, log.topics)
357
414
  message = resultToObject(result) as Record<string, unknown>
358
415
  if (message.message) message = message.message as Record<string, unknown> | undefined
416
+ else if (message.encodedMessage) {
417
+ Object.assign(message, decodeMessageV1(message.encodedMessage as BytesLike))
418
+ }
359
419
  if (message) break
360
420
  } catch (_) {
361
421
  // try next fragment
362
422
  }
363
423
  }
364
424
  if (!message) return
365
- if (!isHexString(message.sender, 20)) throw new CCIPMessageDecodeError('invalid sender')
366
-
367
- if (message.header) {
368
- // CCIPMessage_V1_6
369
- Object.assign(message, message.header)
370
- delete message.header
371
- }
372
-
373
- const sourceFamily = networkInfo(
374
- (message as { sourceChainSelector: bigint }).sourceChainSelector,
375
- ).family
376
- let destFamily: ChainFamily = ChainFamily.EVM
377
- if ((message as { destChainSelector?: bigint }).destChainSelector) {
378
- destFamily = networkInfo((message as { destChainSelector: bigint }).destChainSelector).family
379
- }
380
- // conversions to make any message version be compatible with latest v1.6
381
- message.tokenAmounts = (message.tokenAmounts as Record<string, string | bigint | number>[]).map(
382
- (tokenAmount, i) => {
383
- if ('sourceTokenData' in message) {
384
- // CCIPMessage_V1_2_EVM
385
- try {
386
- tokenAmount = {
387
- ...parseSourceTokenData(
388
- (message as { sourceTokenData: string[] }).sourceTokenData[i]!,
389
- ),
390
- ...tokenAmount,
391
- }
392
- } catch (_) {
393
- // legacy sourceTokenData
394
- }
395
- }
396
- if (typeof tokenAmount.destExecData === 'string' && tokenAmount.destGasAmount == null) {
397
- // CCIPMessage_V1_6_EVM
398
- tokenAmount.destGasAmount = toBigInt(getDataBytes(tokenAmount.destExecData))
399
- }
400
- // Can be undefined if the message is from before v1.5 and failed to parse sourceTokenData
401
- if (tokenAmount.sourcePoolAddress) {
402
- tokenAmount.sourcePoolAddress = decodeAddress(
403
- tokenAmount.sourcePoolAddress as string,
404
- sourceFamily,
405
- )
406
- }
407
- if (tokenAmount.destTokenAddress) {
408
- tokenAmount.destTokenAddress = decodeAddress(
409
- tokenAmount.destTokenAddress as string,
410
- destFamily,
411
- )
412
- }
413
- return tokenAmount
414
- },
415
- )
416
- message.sender = decodeAddress(message.sender, sourceFamily)
417
- message.feeToken = decodeAddress(message.feeToken as string, sourceFamily)
418
- message.receiver = decodeAddress(message.receiver as string, destFamily)
419
- if (message.extraArgs) {
420
- // v1.6+
421
- const parsed = this.decodeExtraArgs(message.extraArgs as string)
422
- if (!parsed) throw new CCIPExtraArgsParseError(message.extraArgs as string)
423
- const { _tag, ...rest } = parsed
424
- // merge parsed extraArgs to any family in message root object
425
- Object.assign(message, rest)
426
- } else if (message.nonce === 0n) {
427
- // v1.2..v1.5 targets EVM only; extraArgs is not explicit, gasLimit is already in
428
- // message body, allowOutOfOrderExecution (in v1.5) was present only as nonce=0
429
- message.allowOutOfOrderExecution = true
430
- }
431
- return message as CCIPMessage
425
+ return decodeMessage(message)
432
426
  }
433
427
 
434
428
  /**
@@ -609,13 +603,23 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
609
603
  return router as string
610
604
  }
611
605
  case CCIPVersion.V1_6: {
612
- onRampABI = OnRamp_1_6_ABI
613
- const contract = new Contract(onRamp, onRampABI, this.provider) as unknown as TypedContract<
614
- typeof onRampABI
615
- >
606
+ const contract = new Contract(
607
+ onRamp,
608
+ interfaces.OnRamp_v1_6,
609
+ this.provider,
610
+ ) as unknown as TypedContract<typeof OnRamp_1_6_ABI>
616
611
  const [, , router] = await contract.getDestChainConfig(destChainSelector)
617
612
  return router as string
618
613
  }
614
+ case CCIPVersion.V2_0: {
615
+ const contract = new Contract(
616
+ onRamp,
617
+ interfaces.OnRamp_v2_0,
618
+ this.provider,
619
+ ) as unknown as TypedContract<typeof OnRamp_2_0_ABI>
620
+ const { router } = await contract.getDestChainConfig(destChainSelector)
621
+ return router as string
622
+ }
619
623
  default:
620
624
  throw new CCIPVersionUnsupportedError(version)
621
625
  }
@@ -642,8 +646,11 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
642
646
  ;({ router } = await contract.getDynamicConfig())
643
647
  break
644
648
  }
645
- case CCIPVersion.V1_6: {
649
+ case CCIPVersion.V1_6:
646
650
  offRampABI = OffRamp_1_6_ABI
651
+ // falls through
652
+ case CCIPVersion.V2_0: {
653
+ offRampABI = OffRamp_2_0_ABI
647
654
  const contract = new Contract(
648
655
  offRamp,
649
656
  offRampABI,
@@ -692,10 +699,10 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
692
699
  }
693
700
 
694
701
  /**
695
- * {@inheritDoc Chain.getOnRampForOffRamp}
702
+ * {@inheritDoc Chain.getOnRampsForOffRamp}
696
703
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
697
704
  */
698
- async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
705
+ async getOnRampsForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string[]> {
699
706
  const [, version] = await this.typeAndVersion(offRamp)
700
707
  let offRampABI
701
708
  switch (version) {
@@ -710,7 +717,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
710
717
  this.provider,
711
718
  ) as unknown as TypedContract<typeof offRampABI>
712
719
  const { onRamp } = await contract.getStaticConfig()
713
- return onRamp as string
720
+ return [onRamp as string]
714
721
  }
715
722
  case CCIPVersion.V1_6: {
716
723
  offRampABI = OffRamp_1_6_ABI
@@ -720,7 +727,19 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
720
727
  this.provider,
721
728
  ) as unknown as TypedContract<typeof offRampABI>
722
729
  const { onRamp } = await contract.getSourceChainConfig(sourceChainSelector)
723
- return decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)
730
+ if (!onRamp || onRamp.match(/^(0x)?0*$/i)) return []
731
+ return [decodeOnRampAddress(onRamp, networkInfo(sourceChainSelector).family)]
732
+ }
733
+ case CCIPVersion.V2_0: {
734
+ offRampABI = OffRamp_2_0_ABI
735
+ const contract = new Contract(
736
+ offRamp,
737
+ offRampABI,
738
+ this.provider,
739
+ ) as unknown as TypedContract<typeof offRampABI>
740
+ const { onRamps } = await contract.getSourceChainConfig(sourceChainSelector)
741
+ const sourceFamily = networkInfo(sourceChainSelector).family
742
+ return onRamps.map((onRamp) => decodeOnRampAddress(onRamp, sourceFamily))
724
743
  }
725
744
  default:
726
745
  throw new CCIPVersionUnsupportedError(version)
@@ -728,8 +747,19 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
728
747
  }
729
748
 
730
749
  /**
731
- * {@inheritDoc Chain.getCommitStoreForOffRamp}
750
+ * Fetch the CommitStore set in OffRamp config (CCIP v1.5 and earlier).
751
+ * For CCIP v1.6 and later, it should return the offRamp address.
752
+ *
753
+ * @param offRamp - OffRamp contract address
754
+ * @returns Promise resolving to CommitStore address
755
+ *
756
+ * @example Get commit store
757
+ * ```typescript
758
+ * const commitStore = await dest.getCommitStoreForOffRamp(offRampAddress)
759
+ * // For v1.6+, commitStore === offRampAddress
760
+ * ```
732
761
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
762
+ * @internal
733
763
  */
734
764
  async getCommitStoreForOffRamp(offRamp: string): Promise<string> {
735
765
  const [, version] = await this.typeAndVersion(offRamp)
@@ -748,11 +778,8 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
748
778
  const { commitStore } = await contract.getStaticConfig()
749
779
  return commitStore as string
750
780
  }
751
- case CCIPVersion.V1_6: {
752
- return offRamp
753
- }
754
781
  default:
755
- throw new CCIPVersionUnsupportedError(version)
782
+ return offRamp
756
783
  }
757
784
  }
758
785
 
@@ -1012,62 +1039,104 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1012
1039
  let sendTx: TransactionRequest = txs.transactions[txs.transactions.length - 1]!
1013
1040
 
1014
1041
  // approve all tokens (including feeToken, if needed) in parallel
1015
- let nonce = await this.provider.getTransactionCount(sender)
1016
1042
  const responses = await Promise.all(
1017
1043
  approveTxs.map(async (tx: TransactionRequest) => {
1018
- tx.nonce = nonce++
1019
- tx = await wallet.populateTransaction(tx)
1020
- tx.from = undefined
1021
- const response = await submitTransaction(wallet, tx, this.provider)
1022
- this.logger.debug('approve =>', response.hash)
1023
- return response
1044
+ tx.nonce = await this.nextNonce(sender)
1045
+ try {
1046
+ tx = await wallet.populateTransaction(tx)
1047
+ tx.from = undefined
1048
+ const response = await submitTransaction(wallet, tx, this.provider)
1049
+ this.logger.debug('approve =>', response.hash)
1050
+ return response
1051
+ } catch (err) {
1052
+ this.nonces[sender]!--
1053
+ throw err
1054
+ }
1024
1055
  }),
1025
1056
  )
1026
1057
  if (responses.length) await responses[responses.length - 1]!.wait(1, 60_000) // wait last tx nonce to be mined
1027
1058
 
1028
- sendTx.nonce = nonce++
1029
- // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
1030
- sendTx = await wallet.populateTransaction(sendTx)
1031
- sendTx.from = undefined // some signers don't like receiving pre-populated `from`
1032
- const response = await submitTransaction(wallet, sendTx, this.provider)
1059
+ sendTx.nonce = await this.nextNonce(sender)
1060
+ let response
1061
+ try {
1062
+ // sendTx.gasLimit = await this.provider.estimateGas(sendTx)
1063
+ sendTx = await wallet.populateTransaction(sendTx)
1064
+ sendTx.from = undefined // some signers don't like receiving pre-populated `from`
1065
+ response = await submitTransaction(wallet, sendTx, this.provider)
1066
+ } catch (err) {
1067
+ this.nonces[sender]!--
1068
+ throw err
1069
+ }
1033
1070
  this.logger.debug('ccipSend =>', response.hash)
1034
- await response.wait(1, 60_000)
1035
- return (await this.getMessagesInTx(await this.getTransaction(response.hash)))[0]!
1036
- }
1037
-
1038
- /** {@inheritDoc Chain.getOffchainTokenData} */
1039
- getOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
1040
- return fetchEVMOffchainTokenData(request, this)
1071
+ const tx = (await response.wait(1, 60_000))!
1072
+ return (await this.getMessagesInTx(await this.getTransaction(tx)))[0]!
1041
1073
  }
1042
1074
 
1043
1075
  /**
1044
- * {@inheritDoc Chain.generateUnsignedExecuteReport}
1076
+ * {@inheritDoc Chain.generateUnsignedExecute}
1045
1077
  * @returns array containing one unsigned `manuallyExecute` TransactionRequest object
1046
1078
  * @throws {@link CCIPVersionUnsupportedError} if OffRamp version is not supported
1047
1079
  */
1048
- async generateUnsignedExecuteReport({
1049
- offRamp,
1050
- execReport,
1051
- ...opts
1052
- }: Parameters<Chain['generateUnsignedExecuteReport']>[0]): Promise<UnsignedEVMTx> {
1053
- const [_, version] = await this.typeAndVersion(offRamp)
1080
+ async generateUnsignedExecute(
1081
+ opts: Parameters<Chain['generateUnsignedExecute']>[0],
1082
+ ): Promise<UnsignedEVMTx> {
1083
+ let input: ExecutionInput, offRamp: string
1084
+ if (!('input' in opts)) {
1085
+ if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
1086
+ ;({ offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId))
1087
+ } else {
1088
+ ;({ offRamp, input } = opts)
1089
+ }
1090
+ if ('verifications' in input) {
1091
+ const contract = new Contract(
1092
+ offRamp,
1093
+ interfaces.OffRamp_v2_0,
1094
+ this.provider,
1095
+ ) as unknown as TypedContract<typeof OffRamp_2_0_ABI>
1096
+
1097
+ const message = decodeMessageV1(input.encodedMessage)
1098
+ const messageId = keccak256(input.encodedMessage)
1099
+ // `execute` doesn't revert on failure, so we need to estimate using `executeSingleMessage`
1100
+ const txGasLimit = await contract.executeSingleMessage.estimateGas(
1101
+ {
1102
+ ...message,
1103
+ executionGasLimit: BigInt(message.executionGasLimit),
1104
+ ccipReceiveGasLimit: BigInt(message.ccipReceiveGasLimit),
1105
+ finality: BigInt(message.finality),
1106
+ },
1107
+ messageId,
1108
+ input.verifications.map(({ destAddress }) => destAddress),
1109
+ input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1110
+ BigInt(opts.gasLimit ?? 0),
1111
+ { from: offRamp }, // internal method
1112
+ )
1113
+ const execTx = await contract.execute.populateTransaction(
1114
+ input.encodedMessage,
1115
+ input.verifications.map(({ destAddress }) => destAddress),
1116
+ input.verifications.map(({ ccvData }) => hexlify(ccvData)),
1117
+ BigInt(opts.gasLimit ?? 0),
1118
+ )
1119
+ execTx.gasLimit = txGasLimit + 40000n // plus `execute`'s overhead
1120
+ return { family: ChainFamily.EVM, transactions: [execTx] }
1121
+ }
1054
1122
 
1055
1123
  let manualExecTx
1056
- const offchainTokenData = execReport.offchainTokenData.map(encodeEVMOffchainTokenData)
1124
+ const [_, version] = await this.typeAndVersion(offRamp)
1125
+ const offchainTokenData = input.offchainTokenData.map(encodeEVMOffchainTokenData)
1057
1126
 
1058
1127
  switch (version) {
1059
1128
  case CCIPVersion.V1_2: {
1060
1129
  const contract = new Contract(
1061
1130
  offRamp,
1062
- EVM2EVMOffRamp_1_2_ABI,
1131
+ interfaces.EVM2EVMOffRamp_v1_2,
1063
1132
  this.provider,
1064
1133
  ) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
1065
1134
  const gasOverride = BigInt(opts.gasLimit ?? 0)
1066
1135
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1067
1136
  {
1068
- ...execReport,
1069
- proofs: execReport.proofs.map((d) => hexlify(d)),
1070
- messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_2>],
1137
+ ...input,
1138
+ proofs: input.proofs.map((d) => hexlify(d)),
1139
+ messages: [input.message as CCIPMessage<typeof CCIPVersion.V1_2>],
1071
1140
  offchainTokenData: [offchainTokenData],
1072
1141
  },
1073
1142
  [gasOverride],
@@ -1077,20 +1146,20 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1077
1146
  case CCIPVersion.V1_5: {
1078
1147
  const contract = new Contract(
1079
1148
  offRamp,
1080
- EVM2EVMOffRamp_1_5_ABI,
1149
+ interfaces.EVM2EVMOffRamp_v1_5,
1081
1150
  this.provider,
1082
1151
  ) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_5_ABI>
1083
1152
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1084
1153
  {
1085
- ...execReport,
1086
- proofs: execReport.proofs.map((d) => hexlify(d)),
1087
- messages: [execReport.message as CCIPMessage<typeof CCIPVersion.V1_5>],
1154
+ ...input,
1155
+ proofs: input.proofs.map((d) => hexlify(d)),
1156
+ messages: [input.message as CCIPMessage<typeof CCIPVersion.V1_5>],
1088
1157
  offchainTokenData: [offchainTokenData],
1089
1158
  },
1090
1159
  [
1091
1160
  {
1092
1161
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1093
- tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
1162
+ tokenGasOverrides: input.message.tokenAmounts.map(() =>
1094
1163
  BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1095
1164
  ),
1096
1165
  },
@@ -1100,30 +1169,32 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1100
1169
  }
1101
1170
  case CCIPVersion.V1_6: {
1102
1171
  // normalize message
1103
- const sender = zeroPadValue(getAddressBytes(execReport.message.sender), 32)
1104
- const tokenAmounts = (execReport.message as CCIPMessage_V1_6_EVM).tokenAmounts.map(
1105
- (ta) => ({
1106
- ...ta,
1107
- sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
1108
- extraData: hexlify(getDataBytes(ta.extraData)),
1109
- }),
1110
- )
1172
+ const senderBytes = getAddressBytes(input.message.sender)
1173
+ // Addresses ≤32 bytes (EVM 20B, Aptos/Solana/Sui 32B) are zero-padded to 32 bytes;
1174
+ // Addresses >32 bytes (e.g., TON 36B) are used as raw bytes without padding
1175
+ const sender =
1176
+ senderBytes.length <= 32 ? zeroPadValue(senderBytes, 32) : hexlify(senderBytes)
1177
+ const tokenAmounts = (input.message as CCIPMessage_V1_6_EVM).tokenAmounts.map((ta) => ({
1178
+ ...ta,
1179
+ sourcePoolAddress: zeroPadValue(getAddressBytes(ta.sourcePoolAddress), 32),
1180
+ extraData: hexlify(getDataBytes(ta.extraData)),
1181
+ }))
1111
1182
  const message = {
1112
- ...(execReport.message as CCIPMessage_V1_6_EVM),
1183
+ ...(input.message as CCIPMessage_V1_6_EVM),
1113
1184
  sender,
1114
1185
  tokenAmounts,
1115
1186
  }
1116
1187
  const contract = new Contract(
1117
1188
  offRamp,
1118
- OffRamp_1_6_ABI,
1189
+ interfaces.OffRamp_v1_6,
1119
1190
  this.provider,
1120
1191
  ) as unknown as TypedContract<typeof OffRamp_1_6_ABI>
1121
1192
  manualExecTx = await contract.manuallyExecute.populateTransaction(
1122
1193
  [
1123
1194
  {
1124
- ...execReport,
1125
- proofs: execReport.proofs.map((p) => hexlify(p)),
1126
- sourceChainSelector: execReport.message.sourceChainSelector,
1195
+ ...input,
1196
+ proofs: input.proofs.map((p) => hexlify(p)),
1197
+ sourceChainSelector: input.message.sourceChainSelector,
1127
1198
  messages: [
1128
1199
  {
1129
1200
  ...message,
@@ -1143,7 +1214,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1143
1214
  [
1144
1215
  {
1145
1216
  receiverExecutionGasLimit: BigInt(opts.gasLimit ?? 0),
1146
- tokenGasOverrides: execReport.message.tokenAmounts.map(() =>
1217
+ tokenGasOverrides: input.message.tokenAmounts.map(() =>
1147
1218
  BigInt(opts.tokensGasLimit ?? opts.gasLimit ?? 0),
1148
1219
  ),
1149
1220
  },
@@ -1159,23 +1230,28 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1159
1230
  }
1160
1231
 
1161
1232
  /**
1162
- * {@inheritDoc Chain.executeReport}
1233
+ * {@inheritDoc Chain.execute}
1163
1234
  * @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
1164
1235
  * @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
1165
1236
  * @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
1166
1237
  */
1167
- async executeReport(opts: Parameters<Chain['executeReport']>[0]) {
1238
+ async execute(opts: Parameters<Chain['execute']>[0]) {
1168
1239
  const wallet = opts.wallet
1169
1240
  if (!isSigner(wallet)) throw new CCIPWalletInvalidError(wallet)
1170
1241
 
1171
- const unsignedTxs = await this.generateUnsignedExecuteReport({
1242
+ const unsignedTxs = await this.generateUnsignedExecute({
1172
1243
  ...opts,
1173
1244
  payer: await wallet.getAddress(),
1174
1245
  })
1175
- const unsignedTx = await wallet.populateTransaction(unsignedTxs.transactions[0]!)
1176
- unsignedTx.from = undefined // some signers don't like receiving pre-populated `from`
1177
- const response = await submitTransaction(wallet, unsignedTx, this.provider)
1246
+
1247
+ const unsignedTx: TransactionRequest = unsignedTxs.transactions[0]!
1248
+ unsignedTx.nonce = await this.nextNonce(await wallet.getAddress())
1249
+ const populatedTx = await wallet.populateTransaction(unsignedTx)
1250
+ populatedTx.from = undefined // some signers don't like receiving pre-populated `from`
1251
+
1252
+ const response = await submitTransaction(wallet, populatedTx, this.provider)
1178
1253
  this.logger.debug('manuallyExecute =>', response.hash)
1254
+
1179
1255
  const receipt = await response.wait(1, 60_000)
1180
1256
  if (!receipt?.hash) throw new CCIPExecTxNotConfirmedError(response.hash)
1181
1257
  if (!receipt.status) throw new CCIPExecTxRevertedError(response.hash)
@@ -1407,6 +1483,64 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1407
1483
  )
1408
1484
  }
1409
1485
 
1486
+ /** {@inheritDoc Chain.getVerifications} */
1487
+ override async getVerifications(
1488
+ opts: Parameters<Chain['getVerifications']>[0],
1489
+ ): Promise<CCIPVerifications> {
1490
+ const { offRamp, request } = opts
1491
+ if (request.lane.version >= CCIPVersion.V2_0) {
1492
+ const message = request.message as CCIPMessage_V2_0
1493
+ if (!message.encodedMessage)
1494
+ throw new CCIPNotImplementedError(`CCIPAPIClient getMessageById v2 encodedMessage`)
1495
+ const contract = new Contract(
1496
+ offRamp,
1497
+ interfaces.OffRamp_v2_0,
1498
+ this.provider,
1499
+ ) as unknown as TypedContract<typeof OffRamp_2_0_ABI>
1500
+ const ccvs = await contract.getCCVsForMessage(message.encodedMessage)
1501
+ const [requiredCCVs, optionalCCVs, optionalThreshold] = ccvs.map(
1502
+ resultToObject,
1503
+ ) as unknown as CleanAddressable<typeof ccvs>
1504
+ const verificationPolicy = {
1505
+ requiredCCVs,
1506
+ optionalCCVs,
1507
+ optionalThreshold: Number(optionalThreshold),
1508
+ }
1509
+
1510
+ if (this.apiClient) {
1511
+ const apiRes = await this.apiClient.getMessageById(request.message.messageId)
1512
+ if ('verifiers' in apiRes.message) {
1513
+ const verifiers = apiRes.message.verifiers as {
1514
+ items: {
1515
+ destAddress: string
1516
+ sourceAddress: string
1517
+ verification: { data: string; timestamp: string }
1518
+ }[]
1519
+ }
1520
+ return {
1521
+ verificationPolicy,
1522
+ verifications: verifiers.items.map((item) => ({
1523
+ destAddress: item.destAddress,
1524
+ sourceAddress: item.sourceAddress,
1525
+ ccvData: item.verification.data,
1526
+ timestamp: new Date(item.verification.timestamp).getTime() / 1e3,
1527
+ })),
1528
+ }
1529
+ }
1530
+ }
1531
+
1532
+ const url = `${CCV_INDEXER_URL}/v1/verifierresults/${request.message.messageId}`
1533
+ const res = await fetch(url)
1534
+ const json = await res.json()
1535
+ return json as CCIPVerifications
1536
+ } else if (request.lane.version < CCIPVersion.V1_6) {
1537
+ // v1.2..v1.5 EVM (only) have separate CommitStore
1538
+ opts.offRamp = await this.getCommitStoreForOffRamp(opts.offRamp)
1539
+ }
1540
+ // fallback <=v1.6
1541
+ return super.getVerifications(opts)
1542
+ }
1543
+
1410
1544
  /** {@inheritDoc Chain.getExecutionReceipts} */
1411
1545
  override async *getExecutionReceipts(
1412
1546
  opts: Parameters<Chain['getExecutionReceipts']>[0],
@@ -1426,10 +1560,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
1426
1560
  // onlyFallback: false,
1427
1561
  }
1428
1562
  } else /* >= V1.6 */ {
1563
+ const topicHash =
1564
+ version === CCIPVersion.V1_6
1565
+ ? interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged')!.topicHash
1566
+ : interfaces.OffRamp_v2_0.getEvent('ExecutionStateChanged')!.topicHash
1429
1567
  opts_ = {
1430
1568
  ...opts,
1431
1569
  topics: [
1432
- interfaces.OffRamp_v1_6.getEvent('ExecutionStateChanged')!.topicHash,
1570
+ topicHash,
1433
1571
  sourceChainSelector ? toBeHex(sourceChainSelector, 32) : null,
1434
1572
  null,
1435
1573
  messageId ?? null,