@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
@@ -1,21 +1,310 @@
1
- import type { AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype'
2
- import type { Addressable, Result } from 'ethers'
1
+ import type {
2
+ AbiParameterToPrimitiveType,
3
+ AbiParametersToPrimitiveTypes,
4
+ ExtractAbiEvent,
5
+ } from 'abitype'
6
+ import {
7
+ type Addressable,
8
+ type BytesLike,
9
+ type Result,
10
+ dataSlice,
11
+ hexlify,
12
+ toBigInt,
13
+ toNumber,
14
+ } from 'ethers'
15
+ import type { Simplify } from 'type-fest'
3
16
 
17
+ import { CCIPMessageDecodeError } from '../errors/index.ts'
4
18
  import type { EVMExtraArgsV2 } from '../extra-args.ts'
5
- import type { CCIPVersion, MergeArrayElements } from '../types.ts'
19
+ import type { CCIPVersion, ChainFamily, MergeArrayElements } from '../types.ts'
20
+ import { decodeAddress, getDataBytes, networkInfo } from '../utils.ts'
6
21
  import type EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
7
22
  import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
23
+ import type OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
8
24
  import { defaultAbiCoder } from './const.ts'
9
25
 
10
26
  /** Utility type that cleans up address types to just `string`. */
11
27
  export type CleanAddressable<T> = T extends string | Addressable
12
28
  ? string
13
- : T extends Record<string, unknown>
29
+ : T extends { [K: string]: unknown } | [...unknown[]]
14
30
  ? { [K in keyof T]: CleanAddressable<T[K]> }
15
- : T extends readonly unknown[]
16
- ? readonly CleanAddressable<T[number]>[]
31
+ : T extends { readonly [K: string]: unknown } | readonly [...unknown[]]
32
+ ? { readonly [K in keyof T]: CleanAddressable<T[K]> }
17
33
  : T
18
34
 
35
+ /** Token transfer in MessageV1 format. */
36
+ export type TokenTransferV1 = {
37
+ amount: bigint
38
+ sourcePoolAddress: string
39
+ sourceTokenAddress: string
40
+ destTokenAddress: string
41
+ tokenReceiver: string
42
+ extraData: string
43
+ }
44
+
45
+ /** MessageV1 struct matching the Solidity MessageV1Codec format. */
46
+ export type MessageV1 = {
47
+ sourceChainSelector: bigint
48
+ destChainSelector: bigint
49
+ messageNumber: bigint
50
+ executionGasLimit: number
51
+ ccipReceiveGasLimit: number
52
+ finality: number
53
+ ccvAndExecutorHash: string
54
+ onRampAddress: string
55
+ offRampAddress: string
56
+ sender: string
57
+ receiver: string
58
+ destBlob: string
59
+ tokenTransfer: readonly TokenTransferV1[]
60
+ data: string
61
+ }
62
+
63
+ /**
64
+ * Decodes a TokenTransferV1 from bytes.
65
+ * @param encoded - The encoded bytes.
66
+ * @param offset - The starting offset.
67
+ * @param sourceFamily - The source chain family for source addresses.
68
+ * @param destFamily - The destination chain family for dest addresses.
69
+ * @returns The decoded token transfer and the new offset.
70
+ */
71
+ function decodeTokenTransferV1(
72
+ encoded: Uint8Array,
73
+ offset: number,
74
+ sourceFamily: ChainFamily,
75
+ destFamily: ChainFamily,
76
+ ): { tokenTransfer: TokenTransferV1; newOffset: number } {
77
+ // version (1 byte)
78
+ if (offset >= encoded.length) throw new CCIPMessageDecodeError('TOKEN_TRANSFER_VERSION')
79
+ const version = encoded[offset++]!
80
+ if (version !== 1) throw new CCIPMessageDecodeError(`Invalid encoding version: ${version}`)
81
+
82
+ // amount (32 bytes)
83
+ if (offset + 32 > encoded.length) throw new CCIPMessageDecodeError('TOKEN_TRANSFER_AMOUNT')
84
+ const amount = toBigInt(dataSlice(encoded, offset, offset + 32))
85
+ offset += 32
86
+
87
+ // sourcePoolAddressLength and sourcePoolAddress
88
+ if (offset >= encoded.length) {
89
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_SOURCE_POOL_LENGTH')
90
+ }
91
+ const sourcePoolAddressLength = encoded[offset++]!
92
+ if (offset + sourcePoolAddressLength > encoded.length) {
93
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_SOURCE_POOL_CONTENT')
94
+ }
95
+ const sourcePoolAddress = decodeAddress(
96
+ dataSlice(encoded, offset, offset + sourcePoolAddressLength),
97
+ sourceFamily,
98
+ )
99
+ offset += sourcePoolAddressLength
100
+
101
+ // sourceTokenAddressLength and sourceTokenAddress
102
+ if (offset >= encoded.length) {
103
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_SOURCE_TOKEN_LENGTH')
104
+ }
105
+ const sourceTokenAddressLength = encoded[offset++]!
106
+ if (offset + sourceTokenAddressLength > encoded.length) {
107
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_SOURCE_TOKEN_CONTENT')
108
+ }
109
+ const sourceTokenAddress = decodeAddress(
110
+ dataSlice(encoded, offset, offset + sourceTokenAddressLength),
111
+ sourceFamily,
112
+ )
113
+ offset += sourceTokenAddressLength
114
+
115
+ // destTokenAddressLength and destTokenAddress
116
+ if (offset >= encoded.length) {
117
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_DEST_TOKEN_LENGTH')
118
+ }
119
+ const destTokenAddressLength = encoded[offset++]!
120
+ if (offset + destTokenAddressLength > encoded.length) {
121
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_DEST_TOKEN_CONTENT')
122
+ }
123
+ const destTokenAddress = decodeAddress(
124
+ dataSlice(encoded, offset, offset + destTokenAddressLength),
125
+ destFamily,
126
+ )
127
+ offset += destTokenAddressLength
128
+
129
+ // tokenReceiverLength and tokenReceiver
130
+ if (offset >= encoded.length) {
131
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_TOKEN_RECEIVER_LENGTH')
132
+ }
133
+ const tokenReceiverLength = encoded[offset++]!
134
+ if (offset + tokenReceiverLength > encoded.length) {
135
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_TOKEN_RECEIVER_CONTENT')
136
+ }
137
+ const tokenReceiver = decodeAddress(
138
+ dataSlice(encoded, offset, offset + tokenReceiverLength),
139
+ destFamily,
140
+ )
141
+ offset += tokenReceiverLength
142
+
143
+ // extraDataLength and extraData
144
+ if (offset + 2 > encoded.length) {
145
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_EXTRA_DATA_LENGTH')
146
+ }
147
+ const extraDataLength = toNumber(dataSlice(encoded, offset, offset + 2))
148
+ offset += 2
149
+ if (offset + extraDataLength > encoded.length) {
150
+ throw new CCIPMessageDecodeError('TOKEN_TRANSFER_EXTRA_DATA_CONTENT')
151
+ }
152
+ const extraData = hexlify(dataSlice(encoded, offset, offset + extraDataLength))
153
+ offset += extraDataLength
154
+
155
+ return {
156
+ tokenTransfer: {
157
+ amount,
158
+ sourcePoolAddress,
159
+ sourceTokenAddress,
160
+ destTokenAddress,
161
+ tokenReceiver,
162
+ extraData,
163
+ },
164
+ newOffset: offset,
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Decodes a MessageV1 from bytes following the v1 protocol format.
170
+ * @param encodedMessage - The encoded message bytes to decode.
171
+ * @returns The decoded MessageV1 struct.
172
+ */
173
+ export function decodeMessageV1(encodedMessage: BytesLike): MessageV1 {
174
+ const MESSAGE_V1_BASE_SIZE = 77
175
+ const encoded = getDataBytes(encodedMessage)
176
+
177
+ if (encoded.length < MESSAGE_V1_BASE_SIZE) throw new CCIPMessageDecodeError('MESSAGE_MIN_SIZE')
178
+
179
+ const version = encoded[0]!
180
+ if (version !== 1) throw new CCIPMessageDecodeError(`Invalid encoding version: ${version}`)
181
+
182
+ // sourceChainSelector (8 bytes, big endian)
183
+ const sourceChainSelector = toBigInt(dataSlice(encoded, 1, 9))
184
+
185
+ // destChainSelector (8 bytes, big endian)
186
+ const destChainSelector = toBigInt(dataSlice(encoded, 9, 17))
187
+
188
+ // Get chain families for address decoding
189
+ const sourceNetworkInfo = networkInfo(sourceChainSelector)
190
+ const destNetworkInfo = networkInfo(destChainSelector)
191
+ const sourceFamily = sourceNetworkInfo.family
192
+ const destFamily = destNetworkInfo.family
193
+
194
+ // messageNumber (8 bytes, big endian)
195
+ const messageNumber = toBigInt(dataSlice(encoded, 17, 25))
196
+
197
+ // executionGasLimit (4 bytes, big endian)
198
+ const executionGasLimit = toNumber(dataSlice(encoded, 25, 29))
199
+
200
+ // ccipReceiveGasLimit (4 bytes, big endian)
201
+ const ccipReceiveGasLimit = toNumber(dataSlice(encoded, 29, 33))
202
+
203
+ // finality (2 bytes, big endian)
204
+ const finality = toNumber(dataSlice(encoded, 33, 35))
205
+
206
+ // ccvAndExecutorHash (32 bytes)
207
+ const ccvAndExecutorHash = hexlify(dataSlice(encoded, 35, 67))
208
+
209
+ // onRampAddressLength and onRampAddress
210
+ let offset = 67
211
+ if (offset >= encoded.length) throw new CCIPMessageDecodeError('MESSAGE_ONRAMP_ADDRESS_LENGTH')
212
+ const onRampAddressLength = encoded[offset++]!
213
+ if (offset + onRampAddressLength > encoded.length) {
214
+ throw new CCIPMessageDecodeError('MESSAGE_ONRAMP_ADDRESS_CONTENT')
215
+ }
216
+ const onRampAddress = decodeAddress(
217
+ dataSlice(encoded, offset, offset + onRampAddressLength),
218
+ sourceFamily,
219
+ )
220
+ offset += onRampAddressLength
221
+
222
+ // offRampAddressLength and offRampAddress
223
+ if (offset >= encoded.length) throw new CCIPMessageDecodeError('MESSAGE_OFFRAMP_ADDRESS_LENGTH')
224
+ const offRampAddressLength = encoded[offset++]!
225
+ if (offset + offRampAddressLength > encoded.length) {
226
+ throw new CCIPMessageDecodeError('MESSAGE_OFFRAMP_ADDRESS_CONTENT')
227
+ }
228
+ const offRampAddress = decodeAddress(
229
+ dataSlice(encoded, offset, offset + offRampAddressLength),
230
+ destFamily,
231
+ )
232
+ offset += offRampAddressLength
233
+
234
+ // senderLength and sender
235
+ if (offset >= encoded.length) throw new CCIPMessageDecodeError('MESSAGE_SENDER_LENGTH')
236
+ const senderLength = encoded[offset++]!
237
+ if (offset + senderLength > encoded.length) {
238
+ throw new CCIPMessageDecodeError('MESSAGE_SENDER_CONTENT')
239
+ }
240
+ const sender = decodeAddress(dataSlice(encoded, offset, offset + senderLength), sourceFamily)
241
+ offset += senderLength
242
+
243
+ // receiverLength and receiver
244
+ if (offset >= encoded.length) throw new CCIPMessageDecodeError('MESSAGE_RECEIVER_LENGTH')
245
+ const receiverLength = encoded[offset++]!
246
+ if (offset + receiverLength > encoded.length) {
247
+ throw new CCIPMessageDecodeError('MESSAGE_RECEIVER_CONTENT')
248
+ }
249
+ const receiver = decodeAddress(dataSlice(encoded, offset, offset + receiverLength), destFamily)
250
+ offset += receiverLength
251
+
252
+ // destBlobLength and destBlob
253
+ if (offset + 2 > encoded.length) throw new CCIPMessageDecodeError('MESSAGE_DEST_BLOB_LENGTH')
254
+ const destBlobLength = toNumber(dataSlice(encoded, offset, offset + 2))
255
+ offset += 2
256
+ if (offset + destBlobLength > encoded.length) {
257
+ throw new CCIPMessageDecodeError('MESSAGE_DEST_BLOB_CONTENT')
258
+ }
259
+ const destBlob = hexlify(dataSlice(encoded, offset, offset + destBlobLength))
260
+ offset += destBlobLength
261
+
262
+ // tokenTransferLength and tokenTransfer
263
+ if (offset + 2 > encoded.length) throw new CCIPMessageDecodeError('MESSAGE_TOKEN_TRANSFER_LENGTH')
264
+ const tokenTransferLength = toNumber(dataSlice(encoded, offset, offset + 2))
265
+ offset += 2
266
+
267
+ // Decode token transfer, which is either 0 or 1
268
+ const tokenTransfer: TokenTransferV1[] = []
269
+ if (tokenTransferLength > 0) {
270
+ const expectedEnd = offset + tokenTransferLength
271
+ const result = decodeTokenTransferV1(encoded, offset, sourceFamily, destFamily)
272
+ tokenTransfer.push(result.tokenTransfer)
273
+ offset = result.newOffset
274
+ if (offset !== expectedEnd) throw new CCIPMessageDecodeError('MESSAGE_TOKEN_TRANSFER_CONTENT')
275
+ }
276
+
277
+ // dataLength and data
278
+ if (offset + 2 > encoded.length) throw new CCIPMessageDecodeError('MESSAGE_DATA_LENGTH')
279
+ const dataLength = toNumber(dataSlice(encoded, offset, offset + 2))
280
+ offset += 2
281
+ if (offset + dataLength > encoded.length) {
282
+ throw new CCIPMessageDecodeError('MESSAGE_DATA_CONTENT')
283
+ }
284
+ const data = hexlify(dataSlice(encoded, offset, offset + dataLength))
285
+ offset += dataLength
286
+
287
+ // Ensure we've consumed all bytes
288
+ if (offset !== encoded.length) throw new CCIPMessageDecodeError('MESSAGE_FINAL_OFFSET')
289
+
290
+ return {
291
+ sourceChainSelector,
292
+ destChainSelector,
293
+ messageNumber,
294
+ executionGasLimit,
295
+ ccipReceiveGasLimit,
296
+ finality,
297
+ ccvAndExecutorHash,
298
+ onRampAddress,
299
+ offRampAddress,
300
+ sender,
301
+ receiver,
302
+ destBlob,
303
+ tokenTransfer,
304
+ data,
305
+ }
306
+ }
307
+
19
308
  // v1.2-v1.5 Message ()
20
309
  type EVM2AnyMessageRequested = CleanAddressable<
21
310
  AbiParametersToPrimitiveTypes<
@@ -50,12 +339,35 @@ export type CCIPMessage_V1_2_EVM = EVM2AnyMessageRequested
50
339
  /** v1.6 EVM specialization with EVMExtraArgsV2 and tokenAmounts.*.destGasAmount. */
51
340
  export type CCIPMessage_V1_6_EVM = CCIPMessage_V1_6 & EVMExtraArgsV2
52
341
 
342
+ type MessageSentV2EventParams = ExtractAbiEvent<typeof OnRamp_2_0_ABI, 'CCIPMessageSent'>['inputs']
343
+ type CCIPMessageSentV2 = {
344
+ [N in MessageSentV2EventParams[number]['name']]: CleanAddressable<
345
+ AbiParameterToPrimitiveType<MessageSentV2EventParams[number] & { readonly name: N }>
346
+ >
347
+ }
348
+
349
+ /**
350
+ * CCIP v2.0 (fka v1.7) Message
351
+ */
352
+ export type CCIPMessage_V2_0 = Simplify<
353
+ CCIPMessageSentV2 &
354
+ Omit<MessageV1, 'tokenTransfer'> & {
355
+ tokenAmounts: MessageV1['tokenTransfer']
356
+ sequenceNumber: MessageV1['messageNumber']
357
+ feeTokenAmount: bigint
358
+ }
359
+ >
360
+
53
361
  /** Union type for CCIP EVM messages across versions. */
54
- export type CCIPMessage_EVM<V extends CCIPVersion = CCIPVersion> = V extends typeof CCIPVersion.V1_2
55
- ? CCIPMessage_V1_2_EVM
56
- : V extends typeof CCIPVersion.V1_5
57
- ? CCIPMessage_V1_5_EVM
58
- : CCIPMessage_V1_6_EVM
362
+ export type CCIPMessage_EVM<V extends CCIPVersion = CCIPVersion> = V extends typeof CCIPVersion.V2_0
363
+ ? CCIPMessage_V2_0
364
+ : V extends typeof CCIPVersion.V1_6
365
+ ? CCIPMessage_V1_6_EVM
366
+ : V extends typeof CCIPVersion.V1_5
367
+ ? CCIPMessage_V1_5_EVM
368
+ : V extends typeof CCIPVersion.V1_2
369
+ ? CCIPMessage_V1_2_EVM
370
+ : never
59
371
 
60
372
  const SourceTokenData =
61
373
  'tuple(bytes sourcePoolAddress, bytes destTokenAddress, bytes extraData, uint64 destGasAmount)'
@@ -63,7 +63,7 @@ export async function fetchEVMOffchainTokenData(
63
63
  )
64
64
  let lbtcTokenData: OffchainTokenData[] = []
65
65
  try {
66
- let tokenAmounts: readonly SourceTokenData[]
66
+ let tokenAmounts
67
67
  if ('sourceTokenData' in request.message) {
68
68
  tokenAmounts = request.message.sourceTokenData.map(parseSourceTokenData)
69
69
  } else {
@@ -179,7 +179,7 @@ async function getUsdcTokenData(
179
179
  * Try to fetch LBTC attestations for transfers, return undefined in position if can't or not required
180
180
  **/
181
181
  async function getLbtcTokenData(
182
- tokenAmounts: readonly SourceTokenData[],
182
+ tokenAmounts: readonly Pick<SourceTokenData, 'extraData'>[],
183
183
  allLogsInRequest: readonly Pick<Log, 'topics' | 'address' | 'data'>[],
184
184
  networkType: NetworkType,
185
185
  { logger = console }: WithLogger = {},
package/src/evm/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TransactionRequest } from 'ethers'
1
+ import { type TransactionRequest, Result } from 'ethers'
2
2
 
3
3
  import type { ChainFamily } from '../types.ts'
4
4
 
@@ -7,5 +7,23 @@ import type { ChainFamily } from '../types.ts'
7
7
  */
8
8
  export type UnsignedEVMTx = {
9
9
  family: typeof ChainFamily.EVM
10
- transactions: Pick<TransactionRequest, 'from' | 'to' | 'data'>[]
10
+ transactions: Pick<TransactionRequest, 'from' | 'to' | 'data' | 'gasLimit'>[]
11
+ }
12
+
13
+ /**
14
+ * Convert a Result or Promise to an object
15
+ * @internal
16
+ */
17
+ export function resultToObject<T>(o: T): T {
18
+ if (o instanceof Promise) return o.then(resultToObject) as T
19
+ if (!(o instanceof Result)) return o
20
+ if (o.length === 0) return o.toArray() as T
21
+ try {
22
+ const obj = o.toObject()
23
+ if (!Object.keys(obj).every((k) => /^_+\d*$/.test(k)))
24
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, resultToObject(v)])) as T
25
+ } catch (_) {
26
+ // fallthrough
27
+ }
28
+ return o.toArray().map(resultToObject) as T
11
29
  }
package/src/execution.ts CHANGED
@@ -1,23 +1,13 @@
1
1
  import { memoize } from 'micro-memoize'
2
2
 
3
- import type { Chain, ChainStatic } from './chain.ts'
3
+ import type { Chain } from './chain.ts'
4
4
  import {
5
5
  CCIPMerkleRootMismatchError,
6
6
  CCIPMessageNotInBatchError,
7
7
  CCIPOffRampNotFoundError,
8
8
  } from './errors/index.ts'
9
9
  import { Tree, getLeafHasher, proofFlagsToBits } from './hasher/index.ts'
10
- import {
11
- type CCIPCommit,
12
- type CCIPExecution,
13
- type CCIPMessage,
14
- type CCIPRequest,
15
- type CCIPVersion,
16
- type ExecutionReport,
17
- type Lane,
18
- type WithLogger,
19
- ExecutionState,
20
- } from './types.ts'
10
+ import type { CCIPMessage, CCIPVersion, Lane, WithLogger } from './types.ts'
21
11
 
22
12
  /**
23
13
  * Pure/sync function to calculate/generate OffRamp.executeManually report for messageIds
@@ -28,6 +18,38 @@ import {
28
18
  * @param merkleRoot - Optional merkleRoot of the CommitReport, for validation
29
19
  * @param ctx - Context for logging
30
20
  * @returns ManualExec report arguments
21
+ * @throws CCIPMessageNotInBatchError - When the messageId is not found in the provided batch
22
+ * @throws CCIPMerkleRootMismatchError - When calculated merkle root doesn't match the provided one
23
+ *
24
+ * @remarks
25
+ * This is a pure/sync function that performs no I/O - all data must be pre-fetched.
26
+ * It builds a merkle tree from the messages, generates a proof for the target messageId,
27
+ * and optionally validates against the provided merkleRoot.
28
+ *
29
+ * The returned proof can be used with `execute` to manually execute a stuck message.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { calculateManualExecProof, EVMChain } from '@chainlink/ccip-sdk'
34
+ *
35
+ * // Fetch the request and all messages in its batch
36
+ * const request = (await source.getMessagesInTx(txHash))[0]
37
+ * const verifications = await dest.getVerifications({ offRamp, request })
38
+ * const messages = await source.getMessagesInBatch(request, commit.report)
39
+ *
40
+ * // Calculate proof for manual execution
41
+ * const proof = calculateManualExecProof(
42
+ * messages,
43
+ * request.lane,
44
+ * request.message.messageId,
45
+ * commit.report.merkleRoot
46
+ * )
47
+ * console.log('Merkle root:', proof.merkleRoot)
48
+ * console.log('Proofs:', proof.proofs)
49
+ * ```
50
+ * @see {@link discoverOffRamp} - Find the OffRamp for manual execution
51
+ * @see {@link execute} - Execute the report on destination chain
52
+ * @see {@link generateUnsignedExecute} - Build unsigned execution tx
31
53
  **/
32
54
  export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
33
55
  messagesInBatch: readonly CCIPMessage<V>[],
@@ -35,7 +57,7 @@ export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
35
57
  messageId: string,
36
58
  merkleRoot?: string,
37
59
  ctx?: WithLogger,
38
- ): Omit<ExecutionReport, 'offchainTokenData' | 'message'> {
60
+ ) {
39
61
  const hasher = getLeafHasher(lane, ctx)
40
62
 
41
63
  const msgIdx = messagesInBatch.findIndex((message) => message.messageId === messageId)
@@ -64,6 +86,30 @@ export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
64
86
  }
65
87
  }
66
88
 
89
+ /**
90
+ * Discover the OffRamp address for a given OnRamp and destination chain.
91
+ * Results are memoized for performance.
92
+ *
93
+ * @param source - Source chain instance.
94
+ * @param dest - Destination chain instance.
95
+ * @param onRamp - OnRamp contract address on source chain.
96
+ * @param ctx - Optional context with logger.
97
+ * @returns OffRamp address on destination chain.
98
+ * @throws CCIPOffRampNotFoundError - When no matching OffRamp is found for the OnRamp
99
+ * @example
100
+ * ```typescript
101
+ * import { discoverOffRamp, EVMChain } from '@chainlink/ccip-sdk'
102
+ *
103
+ * const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
104
+ * const dest = await EVMChain.fromUrl('https://rpc.fuji.avax.network')
105
+ *
106
+ * const offRamp = await discoverOffRamp(source, dest, onRampAddress)
107
+ * console.log('OffRamp on destination:', offRamp)
108
+ * ```
109
+ * @see {@link calculateManualExecProof} - Use with OffRamp for manual execution
110
+ * @see {@link execute} - Execute on destination chain
111
+ * @see {@link getExecutionReceipts} - Check execution status
112
+ */
67
113
  export const discoverOffRamp = memoize(
68
114
  async function discoverOffRamp_(
69
115
  source: Chain,
@@ -77,26 +123,76 @@ export const discoverOffRamp = memoize(
77
123
  dest.network.chainSelector,
78
124
  )
79
125
  for (const offRamp of sourceOffRamps) {
80
- const destOnRamp = await source.getOnRampForOffRamp(offRamp, dest.network.chainSelector)
81
- const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
82
- const destOffRamps = await dest.getOffRampsForRouter(destRouter, source.network.chainSelector)
83
- for (const offRamp of destOffRamps) {
84
- const offRampsOnRamp = await dest.getOnRampForOffRamp(offRamp, source.network.chainSelector)
126
+ let destOnRamps
127
+ try {
128
+ destOnRamps = await source.getOnRampsForOffRamp(offRamp, dest.network.chainSelector)
129
+ } catch (err) {
85
130
  logger.debug(
86
- 'discoverOffRamp: found, from',
87
- {
88
- sourceOnRamp: onRamp,
89
- sourceRouter,
90
- sourceOffRamps,
91
- destOnRamp,
92
- destOffRamps,
93
- offRampsOnRamp,
94
- },
95
- '=',
131
+ 'discoverOffRamp: skipping offRamp',
96
132
  offRamp,
133
+ '(no valid source chain config)',
134
+ err,
97
135
  )
98
- if (offRampsOnRamp === onRamp) {
99
- return offRamp
136
+ continue
137
+ }
138
+ for (const destOnRamp of destOnRamps) {
139
+ const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
140
+ const destOffRamps = await dest.getOffRampsForRouter(
141
+ destRouter,
142
+ source.network.chainSelector,
143
+ )
144
+ for (const offRamp of destOffRamps) {
145
+ let offRampsOnRamps
146
+ try {
147
+ offRampsOnRamps = await dest.getOnRampsForOffRamp(offRamp, source.network.chainSelector)
148
+ } catch (err) {
149
+ logger.debug(
150
+ 'discoverOffRamp: skipping dest offRamp',
151
+ offRamp,
152
+ '(no valid source chain config)',
153
+ err,
154
+ )
155
+ continue
156
+ }
157
+ for (const offRampsOnRamp of offRampsOnRamps) {
158
+ logger.debug(
159
+ 'discoverOffRamp: found, from',
160
+ {
161
+ sourceOnRamp: onRamp,
162
+ sourceRouter,
163
+ sourceOffRamps,
164
+ destOnRamp,
165
+ destOffRamps,
166
+ offRampsOnRamp,
167
+ },
168
+ '=',
169
+ offRamp,
170
+ )
171
+ for (const offRamp of destOffRamps) {
172
+ const offRampsOnRamps = await dest.getOnRampsForOffRamp(
173
+ offRamp,
174
+ source.network.chainSelector,
175
+ )
176
+ for (const offRampsOnRamp of offRampsOnRamps) {
177
+ logger.debug(
178
+ 'discoverOffRamp: found, from',
179
+ {
180
+ sourceOnRamp: onRamp,
181
+ sourceRouter,
182
+ sourceOffRamps,
183
+ destOnRamp,
184
+ destOffRamps,
185
+ offRampsOnRamps,
186
+ },
187
+ '=',
188
+ offRamp,
189
+ )
190
+ if (offRampsOnRamp === onRamp) {
191
+ return offRamp
192
+ }
193
+ }
194
+ }
195
+ }
100
196
  }
101
197
  }
102
198
  }
@@ -107,44 +203,3 @@ export const discoverOffRamp = memoize(
107
203
  [source.network.chainSelector, dest.network.chainSelector, onRamp] as const,
108
204
  },
109
205
  )
110
-
111
- /**
112
- * Generic implementation for fetching ExecutionReceipts for given requests.
113
- * If more than one request is given, may yield them interleaved.
114
- * Completes as soon as there's no more work to be done.
115
- *
116
- * Two possible behaviors:
117
- * - if `startBlock|startTime` is given, pages forward from that block up;
118
- * completes when success (final) receipt is found for all requests (or reach latest block)
119
- * - otherwise, pages backwards and returns only the most recent receipt per request;
120
- * completes when receipts for all requests were seen
121
- *
122
- * @param dest - Provider to page through.
123
- * @param offRamp - OffRamp contract address.
124
- * @param request - CCIP request to search executions for.
125
- * @param commit - Optional commit info to narrow down search.
126
- * @param hints - Optional hints (e.g., `page` for getLogs pagination range).
127
- */
128
- export async function* getExecutionReceipts(
129
- dest: Chain,
130
- offRamp: string,
131
- request: CCIPRequest,
132
- commit?: CCIPCommit,
133
- hints?: { page?: number },
134
- ): AsyncGenerator<CCIPExecution> {
135
- const onlyLast = !commit?.log.blockNumber && !request.tx.timestamp // backwards
136
- for await (const log of dest.getLogs({
137
- startBlock: commit?.log.blockNumber,
138
- startTime: request.tx.timestamp,
139
- address: offRamp,
140
- topics: ['ExecutionStateChanged'],
141
- ...hints,
142
- })) {
143
- const receipt = (dest.constructor as ChainStatic).decodeReceipt(log)
144
- if (!receipt || receipt.messageId !== request.message.messageId) continue
145
-
146
- const timestamp = log.tx?.timestamp ?? (await dest.getBlockTimestamp(log.blockNumber))
147
- yield { receipt, log, timestamp }
148
- if (onlyLast || receipt.state === ExecutionState.Success) break
149
- }
150
- }