@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/logs.ts CHANGED
@@ -183,7 +183,7 @@ async function getFallbackArchiveLogs(
183
183
  * - If undefined (default): paginate main provider only by filter.page
184
184
  * - If false: first try whole range with main provider, then fallback to archive provider
185
185
  * - If true: don't paginate (throw if can't fetch wide range from either provider)
186
- * @param ctx - Context object containing provider, logger and destry$ notify promise
186
+ * @param ctx - Context object containing provider, logger and destroy$ notify promise
187
187
  * @returns Async iterator of logs.
188
188
  */
189
189
  export async function* getEvmLogs(
@@ -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)'
@@ -1,90 +1,5 @@
1
- import { type Addressable, type Log, EventFragment } from 'ethers'
2
-
3
- import { getLbtcAttestation, getUsdcAttestation } from '../offchain.ts'
4
- import type {
5
- CCIPMessage,
6
- CCIPRequest,
7
- NetworkType,
8
- OffchainTokenData,
9
- WithLogger,
10
- } from '../types.ts'
11
- import { networkInfo } from '../utils.ts'
12
- import { defaultAbiCoder, interfaces, requestsFragments } from './const.ts'
13
- import { type SourceTokenData, parseSourceTokenData } from './messages.ts'
14
-
15
- const BURNED_EVENT_1_5 = interfaces.TokenPool_v1_5.getEvent('Burned')!
16
- const BURNED_EVENT_1_6 = interfaces.TokenPool_v1_6.getEvent('LockedOrBurned')!
17
- const BURNED_EVENT_TOPIC_HASHES = new Set([BURNED_EVENT_1_5.topicHash, BURNED_EVENT_1_6.topicHash])
18
-
19
- const USDC_EVENT = EventFragment.from('MessageSent(bytes message)')
20
- const TRANSFER_EVENT = EventFragment.from('Transfer(address from, address to, uint256 value)')
21
-
22
- export const LBTC_EVENT = EventFragment.from(
23
- 'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash, bytes payload)',
24
- )
25
- export const LBTC_EVENT_V2 = EventFragment.from(
26
- 'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash)',
27
- )
28
- const LBTC_EVENTS_HASHES = new Set([LBTC_EVENT.topicHash, LBTC_EVENT_V2.topicHash])
29
-
30
- /**
31
- * Fetch offchain token data for all transfers in request
32
- *
33
- * @param request - Request (or subset of) to fetch offchainTokenData for
34
- * @returns Array of byte arrays, one per transfer in request
35
- */
36
- export async function fetchEVMOffchainTokenData(
37
- request: Pick<CCIPRequest, 'tx'> & {
38
- message: CCIPMessage
39
- log: Pick<CCIPRequest['log'], 'index'>
40
- },
41
- ctx: WithLogger,
42
- ): Promise<OffchainTokenData[]> {
43
- const { networkType } = networkInfo(request.message.sourceChainSelector)
44
- // there's a chance there are other CCIPSendRequested in same tx,
45
- // and they may contain USDC transfers as well, so we select
46
- // any USDC logs after that and before our CCIPSendRequested
47
- const prevCcipRequestIdx =
48
- request.tx.logs.find(
49
- ({ topics, index }) => topics[0]! in requestsFragments && index < request.log.index,
50
- )?.index ?? -1
51
- const usdcRequestLogs = request.tx.logs.filter(
52
- ({ index }) => prevCcipRequestIdx < index && index < request.log.index,
53
- ) as Log[]
54
-
55
- const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(
56
- () => undefined, // default tokenData
57
- )
58
- const usdcTokenData = await getUsdcTokenData(
59
- request.message.tokenAmounts,
60
- usdcRequestLogs,
61
- networkType,
62
- ctx,
63
- )
64
- let lbtcTokenData: OffchainTokenData[] = []
65
- try {
66
- let tokenAmounts: readonly SourceTokenData[]
67
- if ('sourceTokenData' in request.message) {
68
- tokenAmounts = request.message.sourceTokenData.map(parseSourceTokenData)
69
- } else {
70
- tokenAmounts = request.message.tokenAmounts
71
- }
72
- //for lbtc we distinguish logs by hash in event, so we can pass all of them
73
- lbtcTokenData = await getLbtcTokenData(tokenAmounts, request.tx.logs as Log[], networkType, ctx)
74
- } catch (_) {
75
- // pass
76
- }
77
-
78
- for (let i = 0; i < offchainTokenData.length; i++) {
79
- if (usdcTokenData[i]) {
80
- offchainTokenData[i] = usdcTokenData[i]
81
- } else if (lbtcTokenData[i]) {
82
- offchainTokenData[i] = lbtcTokenData[i]
83
- }
84
- }
85
-
86
- return offchainTokenData
87
- }
1
+ import type { OffchainTokenData } from '../types.ts'
2
+ import { defaultAbiCoder } from './const.ts'
88
3
 
89
4
  /**
90
5
  * Encodes offchain token data for EVM execution.
@@ -99,107 +14,3 @@ export function encodeEVMOffchainTokenData(data: OffchainTokenData): string {
99
14
  }
100
15
  return '0x'
101
16
  }
102
-
103
- /**
104
- * Try to fetch USDC attestations for transfers, return undefined in position if can't
105
- *
106
- * @param tokenAmounts - all tokenAmounts to try
107
- * @param allLogsInRequest - all other logs in same tx as CCIPSendRequested
108
- * @param networkType - network type (mainnet or testnet)
109
- * @returns array where each position is either the attestation for that transfer or undefined
110
- **/
111
- async function getUsdcTokenData(
112
- tokenAmounts: CCIPMessage['tokenAmounts'],
113
- allLogsInRequest: Pick<Log, 'topics' | 'address' | 'data'>[],
114
- networkType: NetworkType,
115
- { logger = console }: WithLogger = {},
116
- ): Promise<OffchainTokenData[]> {
117
- const attestations: OffchainTokenData[] = []
118
-
119
- const messageSentPerTokenAndPool = allLogsInRequest.reduce((acc, log, i, arr) => {
120
- // for our MessageSent of interest (USDC-like), the token is the contract
121
- // which emitted a (burn) Transfer immediately before this event, and the pool emitted a Burned
122
- // event 2 events after
123
- const transferLog = arr[i - 1]
124
- const poolLog = arr[i + 2]
125
- if (
126
- log.topics[0] !== USDC_EVENT.topicHash ||
127
- transferLog?.topics[0] !== TRANSFER_EVENT.topicHash ||
128
- !poolLog?.topics.length ||
129
- !BURNED_EVENT_TOPIC_HASHES.has(poolLog.topics[0]!)
130
- ) {
131
- return acc
132
- }
133
- const token = transferLog.address
134
- const pool = poolLog.address
135
- acc.set(token, [...(acc.get(token) ?? []), log])
136
- acc.set(pool, [...(acc.get(pool) ?? []), log])
137
- return acc
138
- }, new Map<string | Addressable, (typeof allLogsInRequest)[number][]>())
139
-
140
- for (const [i, tokenAmount] of tokenAmounts.entries()) {
141
- const tokenOrPool = 'token' in tokenAmount ? tokenAmount.token : tokenAmount.sourcePoolAddress
142
-
143
- // what if there are more USDC transfers of this same token after this one?
144
- const tokenTransfersCountAfter = tokenAmounts.filter(
145
- (ta, j) => ('token' in ta ? ta.token : ta.sourcePoolAddress) === tokenOrPool && j > i,
146
- ).length
147
-
148
- let messageSentLog: (typeof allLogsInRequest)[number] | undefined
149
- const messageSents = messageSentPerTokenAndPool.get(tokenOrPool)
150
- if (messageSents) {
151
- // look from the end (near our request), but skip MessageSents for further transfers
152
- messageSentLog = messageSents[messageSents.length - 1 - tokenTransfersCountAfter]
153
- }
154
-
155
- let tokenData: OffchainTokenData
156
- if (messageSentLog) {
157
- let message
158
- try {
159
- message = defaultAbiCoder.decode(USDC_EVENT.inputs, messageSentLog.data)[0] as string
160
- const attestation = await getUsdcAttestation(message, networkType)
161
- tokenData = {
162
- _tag: 'usdc',
163
- message,
164
- attestation,
165
- }
166
- // encoding of OffchainTokenData to be done as part of Chain.executeReceipt
167
- } catch (err) {
168
- // maybe not a USDC transfer, or not ready
169
- logger.warn(`❌ EVM CCTP: Failed to fetch attestation for message:`, message, err)
170
- }
171
- }
172
- attestations.push(tokenData)
173
- }
174
-
175
- return attestations
176
- }
177
-
178
- /**
179
- * Try to fetch LBTC attestations for transfers, return undefined in position if can't or not required
180
- **/
181
- async function getLbtcTokenData(
182
- tokenAmounts: readonly SourceTokenData[],
183
- allLogsInRequest: readonly Pick<Log, 'topics' | 'address' | 'data'>[],
184
- networkType: NetworkType,
185
- { logger = console }: WithLogger = {},
186
- ): Promise<OffchainTokenData[]> {
187
- const lbtcDepositHashes = new Set(
188
- allLogsInRequest
189
- .filter(({ topics }) => LBTC_EVENTS_HASHES.has(topics[0]!))
190
- .map(({ topics }) => topics[3]),
191
- )
192
- return Promise.all(
193
- tokenAmounts.map(async ({ extraData }) => {
194
- // Attestation is required when SourceTokenData.extraData is 32 bytes long ('0x' + 64 hex chars)
195
- // otherwise attestation is not required
196
- if (lbtcDepositHashes.has(extraData)) {
197
- try {
198
- return { _tag: 'lbtc', extraData, ...(await getLbtcAttestation(extraData, networkType)) }
199
- } catch (err) {
200
- logger.warn(`❌ EVM LBTC: Failed to fetch attestation for message:`, extraData, err)
201
- }
202
- }
203
- }),
204
- )
205
- }
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
  }