@chainlink/ccip-sdk 0.0.0 → 0.90.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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/aptos/exec.d.ts +18 -0
  4. package/dist/aptos/exec.d.ts.map +1 -0
  5. package/dist/aptos/exec.js +55 -0
  6. package/dist/aptos/exec.js.map +1 -0
  7. package/dist/aptos/hasher.d.ts +11 -0
  8. package/dist/aptos/hasher.d.ts.map +1 -0
  9. package/dist/aptos/hasher.js +62 -0
  10. package/dist/aptos/hasher.js.map +1 -0
  11. package/dist/aptos/index.d.ts +92 -0
  12. package/dist/aptos/index.d.ts.map +1 -0
  13. package/dist/aptos/index.js +482 -0
  14. package/dist/aptos/index.js.map +1 -0
  15. package/dist/aptos/logs.d.ts +9 -0
  16. package/dist/aptos/logs.d.ts.map +1 -0
  17. package/dist/aptos/logs.js +167 -0
  18. package/dist/aptos/logs.js.map +1 -0
  19. package/dist/aptos/send.d.ts +11 -0
  20. package/dist/aptos/send.d.ts.map +1 -0
  21. package/dist/aptos/send.js +78 -0
  22. package/dist/aptos/send.js.map +1 -0
  23. package/dist/aptos/token.d.ts +4 -0
  24. package/dist/aptos/token.d.ts.map +1 -0
  25. package/dist/aptos/token.js +134 -0
  26. package/dist/aptos/token.js.map +1 -0
  27. package/dist/aptos/types.d.ts +78 -0
  28. package/dist/aptos/types.d.ts.map +1 -0
  29. package/dist/aptos/types.js +60 -0
  30. package/dist/aptos/types.js.map +1 -0
  31. package/dist/aptos/utils.d.ts +12 -0
  32. package/dist/aptos/utils.d.ts.map +1 -0
  33. package/dist/aptos/utils.js +15 -0
  34. package/dist/aptos/utils.js.map +1 -0
  35. package/dist/chain.d.ts +344 -0
  36. package/dist/chain.d.ts.map +1 -0
  37. package/dist/chain.js +41 -0
  38. package/dist/chain.js.map +1 -0
  39. package/dist/commits.d.ts +25 -0
  40. package/dist/commits.d.ts.map +1 -0
  41. package/dist/commits.js +29 -0
  42. package/dist/commits.js.map +1 -0
  43. package/dist/evm/abi/BurnMintERC677Token.d.ts +602 -0
  44. package/dist/evm/abi/BurnMintERC677Token.d.ts.map +1 -0
  45. package/dist/evm/abi/BurnMintERC677Token.js +488 -0
  46. package/dist/evm/abi/BurnMintERC677Token.js.map +1 -0
  47. package/dist/evm/abi/CommitStore_1_2.d.ts +688 -0
  48. package/dist/evm/abi/CommitStore_1_2.d.ts.map +1 -0
  49. package/dist/evm/abi/CommitStore_1_2.js +638 -0
  50. package/dist/evm/abi/CommitStore_1_2.js.map +1 -0
  51. package/dist/evm/abi/CommitStore_1_5.d.ts +708 -0
  52. package/dist/evm/abi/CommitStore_1_5.d.ts.map +1 -0
  53. package/dist/evm/abi/CommitStore_1_5.js +675 -0
  54. package/dist/evm/abi/CommitStore_1_5.js.map +1 -0
  55. package/dist/evm/abi/FeeQuoter_1_6.d.ts +1770 -0
  56. package/dist/evm/abi/FeeQuoter_1_6.d.ts.map +1 -0
  57. package/dist/evm/abi/FeeQuoter_1_6.js +1904 -0
  58. package/dist/evm/abi/FeeQuoter_1_6.js.map +1 -0
  59. package/dist/evm/abi/LockReleaseTokenPool_1_5.d.ts +1116 -0
  60. package/dist/evm/abi/LockReleaseTokenPool_1_5.d.ts.map +1 -0
  61. package/dist/evm/abi/LockReleaseTokenPool_1_5.js +1096 -0
  62. package/dist/evm/abi/LockReleaseTokenPool_1_5.js.map +1 -0
  63. package/dist/evm/abi/LockReleaseTokenPool_1_5_1.d.ts +1306 -0
  64. package/dist/evm/abi/LockReleaseTokenPool_1_5_1.d.ts.map +1 -0
  65. package/dist/evm/abi/LockReleaseTokenPool_1_5_1.js +1278 -0
  66. package/dist/evm/abi/LockReleaseTokenPool_1_5_1.js.map +1 -0
  67. package/dist/evm/abi/LockReleaseTokenPool_1_6_1.d.ts +1290 -0
  68. package/dist/evm/abi/LockReleaseTokenPool_1_6_1.d.ts.map +1 -0
  69. package/dist/evm/abi/LockReleaseTokenPool_1_6_1.js +1288 -0
  70. package/dist/evm/abi/LockReleaseTokenPool_1_6_1.js.map +1 -0
  71. package/dist/evm/abi/OffRamp_1_2.d.ts +1217 -0
  72. package/dist/evm/abi/OffRamp_1_2.d.ts.map +1 -0
  73. package/dist/evm/abi/OffRamp_1_2.js +1204 -0
  74. package/dist/evm/abi/OffRamp_1_2.js.map +1 -0
  75. package/dist/evm/abi/OffRamp_1_5.d.ts +1271 -0
  76. package/dist/evm/abi/OffRamp_1_5.d.ts.map +1 -0
  77. package/dist/evm/abi/OffRamp_1_5.js +1273 -0
  78. package/dist/evm/abi/OffRamp_1_5.js.map +1 -0
  79. package/dist/evm/abi/OffRamp_1_6.d.ts +1472 -0
  80. package/dist/evm/abi/OffRamp_1_6.d.ts.map +1 -0
  81. package/dist/evm/abi/OffRamp_1_6.js +1529 -0
  82. package/dist/evm/abi/OffRamp_1_6.js.map +1 -0
  83. package/dist/evm/abi/OnRamp_1_2.d.ts +1391 -0
  84. package/dist/evm/abi/OnRamp_1_2.d.ts.map +1 -0
  85. package/dist/evm/abi/OnRamp_1_2.js +1343 -0
  86. package/dist/evm/abi/OnRamp_1_2.js.map +1 -0
  87. package/dist/evm/abi/OnRamp_1_5.d.ts +1443 -0
  88. package/dist/evm/abi/OnRamp_1_5.d.ts.map +1 -0
  89. package/dist/evm/abi/OnRamp_1_5.js +1427 -0
  90. package/dist/evm/abi/OnRamp_1_5.js.map +1 -0
  91. package/dist/evm/abi/OnRamp_1_6.d.ts +796 -0
  92. package/dist/evm/abi/OnRamp_1_6.d.ts.map +1 -0
  93. package/dist/evm/abi/OnRamp_1_6.js +880 -0
  94. package/dist/evm/abi/OnRamp_1_6.js.map +1 -0
  95. package/dist/evm/abi/Router.d.ts +541 -0
  96. package/dist/evm/abi/Router.d.ts.map +1 -0
  97. package/dist/evm/abi/Router.js +508 -0
  98. package/dist/evm/abi/Router.js.map +1 -0
  99. package/dist/evm/abi/TokenAdminRegistry_1_5.d.ts +373 -0
  100. package/dist/evm/abi/TokenAdminRegistry_1_5.d.ts.map +1 -0
  101. package/dist/evm/abi/TokenAdminRegistry_1_5.js +333 -0
  102. package/dist/evm/abi/TokenAdminRegistry_1_5.js.map +1 -0
  103. package/dist/evm/const.d.ts +27 -0
  104. package/dist/evm/const.d.ts.map +1 -0
  105. package/dist/evm/const.js +63 -0
  106. package/dist/evm/const.js.map +1 -0
  107. package/dist/evm/errors.d.ts +36 -0
  108. package/dist/evm/errors.d.ts.map +1 -0
  109. package/dist/evm/errors.js +192 -0
  110. package/dist/evm/errors.js.map +1 -0
  111. package/dist/evm/hasher.d.ts +5 -0
  112. package/dist/evm/hasher.d.ts.map +1 -0
  113. package/dist/evm/hasher.js +116 -0
  114. package/dist/evm/hasher.js.map +1 -0
  115. package/dist/evm/index.d.ts +121 -0
  116. package/dist/evm/index.d.ts.map +1 -0
  117. package/dist/evm/index.js +904 -0
  118. package/dist/evm/index.js.map +1 -0
  119. package/dist/evm/messages.d.ts +35 -0
  120. package/dist/evm/messages.d.ts.map +1 -0
  121. package/dist/evm/messages.js +11 -0
  122. package/dist/evm/messages.js.map +1 -0
  123. package/dist/evm/offchain.d.ts +16 -0
  124. package/dist/evm/offchain.d.ts.map +1 -0
  125. package/dist/evm/offchain.js +142 -0
  126. package/dist/evm/offchain.js.map +1 -0
  127. package/dist/execution.d.ts +80 -0
  128. package/dist/execution.d.ts.map +1 -0
  129. package/dist/execution.js +91 -0
  130. package/dist/execution.js.map +1 -0
  131. package/dist/extra-args.d.ts +45 -0
  132. package/dist/extra-args.d.ts.map +1 -0
  133. package/dist/extra-args.js +44 -0
  134. package/dist/extra-args.js.map +1 -0
  135. package/dist/gas.d.ts +27 -0
  136. package/dist/gas.d.ts.map +1 -0
  137. package/dist/gas.js +80 -0
  138. package/dist/gas.js.map +1 -0
  139. package/dist/hasher/common.d.ts +12 -0
  140. package/dist/hasher/common.d.ts.map +1 -0
  141. package/dist/hasher/common.js +19 -0
  142. package/dist/hasher/common.js.map +1 -0
  143. package/dist/hasher/hasher.d.ts +4 -0
  144. package/dist/hasher/hasher.d.ts.map +1 -0
  145. package/dist/hasher/hasher.js +11 -0
  146. package/dist/hasher/hasher.js.map +1 -0
  147. package/dist/hasher/index.d.ts +4 -0
  148. package/dist/hasher/index.d.ts.map +1 -0
  149. package/dist/hasher/index.js +4 -0
  150. package/dist/hasher/index.js.map +1 -0
  151. package/dist/hasher/merklemulti.d.ts +58 -0
  152. package/dist/hasher/merklemulti.d.ts.map +1 -0
  153. package/dist/hasher/merklemulti.js +257 -0
  154. package/dist/hasher/merklemulti.js.map +1 -0
  155. package/dist/index.d.ts +13 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +13 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/offchain.d.ts +20 -0
  160. package/dist/offchain.d.ts.map +1 -0
  161. package/dist/offchain.js +59 -0
  162. package/dist/offchain.js.map +1 -0
  163. package/dist/requests.d.ts +48 -0
  164. package/dist/requests.d.ts.map +1 -0
  165. package/dist/requests.js +286 -0
  166. package/dist/requests.js.map +1 -0
  167. package/dist/selectors.d.ts +9 -0
  168. package/dist/selectors.d.ts.map +1 -0
  169. package/dist/selectors.js +1330 -0
  170. package/dist/selectors.js.map +1 -0
  171. package/dist/solana/cleanup.d.ts +15 -0
  172. package/dist/solana/cleanup.d.ts.map +1 -0
  173. package/dist/solana/cleanup.js +159 -0
  174. package/dist/solana/cleanup.js.map +1 -0
  175. package/dist/solana/exec.d.ts +15 -0
  176. package/dist/solana/exec.d.ts.map +1 -0
  177. package/dist/solana/exec.js +417 -0
  178. package/dist/solana/exec.js.map +1 -0
  179. package/dist/solana/hasher.d.ts +4 -0
  180. package/dist/solana/hasher.d.ts.map +1 -0
  181. package/dist/solana/hasher.js +81 -0
  182. package/dist/solana/hasher.js.map +1 -0
  183. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts +866 -0
  184. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.d.ts.map +1 -0
  185. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js +866 -0
  186. package/dist/solana/idl/1.6.0/BASE_TOKEN_POOL.js.map +1 -0
  187. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts +949 -0
  188. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.d.ts.map +1 -0
  189. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js +949 -0
  190. package/dist/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.js.map +1 -0
  191. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts +1374 -0
  192. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.d.ts.map +1 -0
  193. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js +1374 -0
  194. package/dist/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js.map +1 -0
  195. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts +104 -0
  196. package/dist/solana/idl/1.6.0/CCIP_COMMON.d.ts.map +1 -0
  197. package/dist/solana/idl/1.6.0/CCIP_COMMON.js +104 -0
  198. package/dist/solana/idl/1.6.0/CCIP_COMMON.js.map +1 -0
  199. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts +2746 -0
  200. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.d.ts.map +1 -0
  201. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js +2746 -0
  202. package/dist/solana/idl/1.6.0/CCIP_OFFRAMP.js.map +1 -0
  203. package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts +2332 -0
  204. package/dist/solana/idl/1.6.0/CCIP_ROUTER.d.ts.map +1 -0
  205. package/dist/solana/idl/1.6.0/CCIP_ROUTER.js +2332 -0
  206. package/dist/solana/idl/1.6.0/CCIP_ROUTER.js.map +1 -0
  207. package/dist/solana/index.d.ts +205 -0
  208. package/dist/solana/index.d.ts.map +1 -0
  209. package/dist/solana/index.js +1085 -0
  210. package/dist/solana/index.js.map +1 -0
  211. package/dist/solana/offchain.d.ts +31 -0
  212. package/dist/solana/offchain.d.ts.map +1 -0
  213. package/dist/solana/offchain.js +152 -0
  214. package/dist/solana/offchain.js.map +1 -0
  215. package/dist/solana/patchBorsh.d.ts +2 -0
  216. package/dist/solana/patchBorsh.d.ts.map +1 -0
  217. package/dist/solana/patchBorsh.js +60 -0
  218. package/dist/solana/patchBorsh.js.map +1 -0
  219. package/dist/solana/send.d.ts +14 -0
  220. package/dist/solana/send.d.ts.map +1 -0
  221. package/dist/solana/send.js +272 -0
  222. package/dist/solana/send.js.map +1 -0
  223. package/dist/solana/types.d.ts +4 -0
  224. package/dist/solana/types.d.ts.map +1 -0
  225. package/dist/solana/types.js +2 -0
  226. package/dist/solana/types.js.map +1 -0
  227. package/dist/solana/utils.d.ts +58 -0
  228. package/dist/solana/utils.d.ts.map +1 -0
  229. package/dist/solana/utils.js +211 -0
  230. package/dist/solana/utils.js.map +1 -0
  231. package/dist/sui/hasher.d.ts +12 -0
  232. package/dist/sui/hasher.d.ts.map +1 -0
  233. package/dist/sui/hasher.js +63 -0
  234. package/dist/sui/hasher.js.map +1 -0
  235. package/dist/sui/index.d.ts +72 -0
  236. package/dist/sui/index.d.ts.map +1 -0
  237. package/dist/sui/index.js +128 -0
  238. package/dist/sui/index.js.map +1 -0
  239. package/dist/sui/types.d.ts +17 -0
  240. package/dist/sui/types.d.ts.map +1 -0
  241. package/dist/sui/types.js +17 -0
  242. package/dist/sui/types.js.map +1 -0
  243. package/dist/supported-chains.d.ts +5 -0
  244. package/dist/supported-chains.d.ts.map +1 -0
  245. package/dist/supported-chains.js +3 -0
  246. package/dist/supported-chains.js.map +1 -0
  247. package/dist/types.d.ts +118 -0
  248. package/dist/types.d.ts.map +1 -0
  249. package/dist/types.js +11 -0
  250. package/dist/types.js.map +1 -0
  251. package/dist/utils.d.ts +117 -0
  252. package/dist/utils.d.ts.map +1 -0
  253. package/dist/utils.js +336 -0
  254. package/dist/utils.js.map +1 -0
  255. package/package.json +66 -8
  256. package/src/aptos/exec.ts +69 -0
  257. package/src/aptos/hasher.ts +92 -0
  258. package/src/aptos/index.ts +660 -0
  259. package/src/aptos/logs.ts +210 -0
  260. package/src/aptos/send.ts +120 -0
  261. package/src/aptos/token.ts +150 -0
  262. package/src/aptos/types.ts +85 -0
  263. package/src/aptos/utils.ts +24 -0
  264. package/src/chain.ts +398 -0
  265. package/src/commits.ts +44 -0
  266. package/src/evm/abi/BurnMintERC677Token.ts +487 -0
  267. package/src/evm/abi/CommitStore_1_2.ts +637 -0
  268. package/src/evm/abi/CommitStore_1_5.ts +674 -0
  269. package/src/evm/abi/FeeQuoter_1_6.ts +1903 -0
  270. package/src/evm/abi/LockReleaseTokenPool_1_5.ts +1095 -0
  271. package/src/evm/abi/LockReleaseTokenPool_1_5_1.ts +1277 -0
  272. package/src/evm/abi/LockReleaseTokenPool_1_6_1.ts +1287 -0
  273. package/src/evm/abi/OffRamp_1_2.ts +1203 -0
  274. package/src/evm/abi/OffRamp_1_5.ts +1272 -0
  275. package/src/evm/abi/OffRamp_1_6.ts +1528 -0
  276. package/src/evm/abi/OnRamp_1_2.ts +1342 -0
  277. package/src/evm/abi/OnRamp_1_5.ts +1426 -0
  278. package/src/evm/abi/OnRamp_1_6.ts +879 -0
  279. package/src/evm/abi/Router.ts +507 -0
  280. package/src/evm/abi/TokenAdminRegistry_1_5.ts +332 -0
  281. package/src/evm/const.ts +69 -0
  282. package/src/evm/errors.ts +212 -0
  283. package/src/evm/hasher.ts +166 -0
  284. package/src/evm/index.ts +1262 -0
  285. package/src/evm/messages.ts +73 -0
  286. package/src/evm/offchain.ts +189 -0
  287. package/src/execution.ts +131 -0
  288. package/src/extra-args.ts +71 -0
  289. package/src/gas.ts +135 -0
  290. package/src/hasher/common.ts +23 -0
  291. package/src/hasher/hasher.ts +12 -0
  292. package/src/hasher/index.ts +3 -0
  293. package/src/hasher/merklemulti.ts +309 -0
  294. package/src/index.ts +51 -0
  295. package/src/offchain.ts +86 -0
  296. package/src/requests.ts +339 -0
  297. package/src/selectors.ts +1340 -0
  298. package/src/solana/cleanup.ts +216 -0
  299. package/src/solana/exec.ts +645 -0
  300. package/src/solana/hasher.ts +104 -0
  301. package/src/solana/idl/1.6.0/BASE_TOKEN_POOL.ts +1734 -0
  302. package/src/solana/idl/1.6.0/BURN_MINT_TOKEN_POOL.ts +1900 -0
  303. package/src/solana/idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts +2750 -0
  304. package/src/solana/idl/1.6.0/CCIP_COMMON.ts +210 -0
  305. package/src/solana/idl/1.6.0/CCIP_OFFRAMP.ts +5494 -0
  306. package/src/solana/idl/1.6.0/CCIP_ROUTER.ts +4671 -0
  307. package/src/solana/index.ts +1454 -0
  308. package/src/solana/offchain.ts +209 -0
  309. package/src/solana/patchBorsh.ts +67 -0
  310. package/src/solana/send.ts +436 -0
  311. package/src/solana/types.ts +6 -0
  312. package/src/solana/utils.ts +272 -0
  313. package/src/sui/hasher.ts +90 -0
  314. package/src/sui/index.ts +198 -0
  315. package/src/sui/types.ts +22 -0
  316. package/src/supported-chains.ts +4 -0
  317. package/src/types.ts +153 -0
  318. package/src/utils.ts +405 -0
  319. package/tsconfig.json +18 -0
@@ -0,0 +1,73 @@
1
+ import type { AbiParametersToPrimitiveTypes, ExtractAbiEvent } from 'abitype'
2
+ import type { Addressable, Result } from 'ethers'
3
+
4
+ import type { EVMExtraArgsV2 } from '../extra-args.ts'
5
+ import type { CCIPVersion, MergeArrayElements } from '../types.ts'
6
+ import type EVM2EVMOnRamp_1_5_ABI from './abi/OnRamp_1_5.ts'
7
+ import type OnRamp_1_6_ABI from './abi/OnRamp_1_6.ts'
8
+ import { defaultAbiCoder } from './const.ts'
9
+
10
+ // addresses often come as `string | Addressable`, this type cleans them up to just `string`
11
+ export type CleanAddressable<T> = T extends string | Addressable
12
+ ? string
13
+ : T extends Record<string, unknown>
14
+ ? { [K in keyof T]: CleanAddressable<T[K]> }
15
+ : T extends readonly unknown[]
16
+ ? readonly CleanAddressable<T[number]>[]
17
+ : T
18
+
19
+ // v1.2-v1.5 Message ()
20
+ type EVM2AnyMessageRequested = CleanAddressable<
21
+ AbiParametersToPrimitiveTypes<
22
+ ExtractAbiEvent<typeof EVM2EVMOnRamp_1_5_ABI, 'CCIPSendRequested'>['inputs']
23
+ >[0]
24
+ >
25
+
26
+ // v1.6+ Message Base (all other dests share this intersection)
27
+ export type CCIPMessage_V1_6 = MergeArrayElements<
28
+ CleanAddressable<
29
+ AbiParametersToPrimitiveTypes<
30
+ ExtractAbiEvent<typeof OnRamp_1_6_ABI, 'CCIPMessageSent'>['inputs']
31
+ >[2]
32
+ >,
33
+ { tokenAmounts: readonly SourceTokenData[] }
34
+ >
35
+
36
+ export type CCIPMessage_V1_5_EVM = MergeArrayElements<
37
+ EVM2AnyMessageRequested,
38
+ {
39
+ header: Omit<CCIPMessage_V1_6['header'], 'destChainSelector'>
40
+ tokenAmounts: readonly SourceTokenData[]
41
+ }
42
+ >
43
+
44
+ export type CCIPMessage_V1_2_EVM = EVM2AnyMessageRequested & {
45
+ header: Omit<CCIPMessage_V1_6['header'], 'destChainSelector'>
46
+ }
47
+
48
+ // v1.6 EVM specialization, extends CCIPMessage_V1_6, plus EVMExtraArgsV2 and tokenAmounts.*.destGasAmount
49
+ export type CCIPMessage_V1_6_EVM = CCIPMessage_V1_6 & EVMExtraArgsV2
50
+
51
+ export type CCIPMessage_EVM<V extends CCIPVersion = CCIPVersion> = V extends typeof CCIPVersion.V1_2
52
+ ? CCIPMessage_V1_2_EVM
53
+ : V extends typeof CCIPVersion.V1_5
54
+ ? CCIPMessage_V1_5_EVM
55
+ : CCIPMessage_V1_6_EVM
56
+
57
+ const SourceTokenData =
58
+ 'tuple(bytes sourcePoolAddress, bytes destTokenAddress, bytes extraData, uint64 destGasAmount)'
59
+ export type SourceTokenData = {
60
+ sourcePoolAddress: string
61
+ destTokenAddress: string
62
+ extraData: string
63
+ destGasAmount: bigint
64
+ }
65
+
66
+ /**
67
+ * parse <=v1.5 `message.sourceTokenData`;
68
+ * v1.6+ already contains this in `message.tokenAmounts`
69
+ */
70
+ export function parseSourceTokenData(data: string): SourceTokenData {
71
+ const decoded = defaultAbiCoder.decode([SourceTokenData], data)
72
+ return (decoded[0] as Result).toObject() as SourceTokenData
73
+ }
@@ -0,0 +1,189 @@
1
+ import { type Addressable, type Log, EventFragment } from 'ethers'
2
+
3
+ import { getLbtcAttestation, getUsdcAttestation } from '../offchain.ts'
4
+ import type { CCIPMessage, CCIPRequest, OffchainTokenData } from '../types.ts'
5
+ import { networkInfo } from '../utils.ts'
6
+ import { defaultAbiCoder, interfaces, requestsFragments } from './const.ts'
7
+ import { type SourceTokenData, parseSourceTokenData } from './messages.ts'
8
+
9
+ const BURNED_EVENT_1_5 = interfaces.TokenPool_v1_5.getEvent('Burned')!
10
+ const BURNED_EVENT_1_6 = interfaces.TokenPool_v1_6.getEvent('LockedOrBurned')!
11
+ const BURNED_EVENT_TOPIC_HASHES = new Set([BURNED_EVENT_1_5.topicHash, BURNED_EVENT_1_6.topicHash])
12
+
13
+ const USDC_EVENT = EventFragment.from('MessageSent(bytes message)')
14
+ const TRANSFER_EVENT = EventFragment.from('Transfer(address from, address to, uint256 value)')
15
+
16
+ export const LBTC_EVENT = EventFragment.from(
17
+ 'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash, bytes payload)',
18
+ )
19
+ export const LBTC_EVENT_V2 = EventFragment.from(
20
+ 'DepositToBridge(address fromAddress, bytes32 toAddress, bytes32 payloadHash)',
21
+ )
22
+ const LBTC_EVENTS_HASHES = new Set([LBTC_EVENT.topicHash, LBTC_EVENT_V2.topicHash])
23
+
24
+ /**
25
+ * Fetch offchain token data for all transfers in request
26
+ *
27
+ * @param request - Request (or subset of) to fetch offchainTokenData for
28
+ * @returns Array of byte arrays, one per transfer in request
29
+ */
30
+ export async function fetchEVMOffchainTokenData(
31
+ request: Pick<CCIPRequest, 'tx'> & {
32
+ message: CCIPMessage
33
+ log: Pick<CCIPRequest['log'], 'index'>
34
+ },
35
+ ): Promise<OffchainTokenData[]> {
36
+ const { isTestnet } = networkInfo(request.message.header.sourceChainSelector)
37
+ // there's a chance there are other CCIPSendRequested in same tx,
38
+ // and they may contain USDC transfers as well, so we select
39
+ // any USDC logs after that and before our CCIPSendRequested
40
+ const prevCcipRequestIdx =
41
+ request.tx.logs.find(
42
+ ({ topics, index }) => topics[0] in requestsFragments && index < request.log.index,
43
+ )?.index ?? -1
44
+ const usdcRequestLogs = request.tx.logs.filter(
45
+ ({ index }) => prevCcipRequestIdx < index && index < request.log.index,
46
+ ) as Log[]
47
+
48
+ const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(
49
+ () => undefined, // default tokenData
50
+ )
51
+ const usdcTokenData = await getUsdcTokenData(
52
+ request.message.tokenAmounts,
53
+ usdcRequestLogs,
54
+ isTestnet,
55
+ )
56
+ let lbtcTokenData: OffchainTokenData[] = []
57
+ try {
58
+ let tokenAmounts: readonly SourceTokenData[]
59
+ if ('sourceTokenData' in request.message) {
60
+ tokenAmounts = request.message.sourceTokenData.map(parseSourceTokenData)
61
+ } else {
62
+ tokenAmounts = request.message.tokenAmounts
63
+ }
64
+ //for lbtc we distinguish logs by hash in event, so we can pass all of them
65
+ lbtcTokenData = await getLbtcTokenData(tokenAmounts, request.tx.logs as Log[], isTestnet)
66
+ } catch (_) {
67
+ // pass
68
+ }
69
+
70
+ for (let i = 0; i < offchainTokenData.length; i++) {
71
+ if (usdcTokenData[i]) {
72
+ offchainTokenData[i] = usdcTokenData[i]
73
+ } else if (lbtcTokenData[i]) {
74
+ offchainTokenData[i] = lbtcTokenData[i]
75
+ }
76
+ }
77
+
78
+ return offchainTokenData
79
+ }
80
+
81
+ export function encodeEVMOffchainTokenData(data: OffchainTokenData): string {
82
+ if (data?._tag === 'usdc') {
83
+ return defaultAbiCoder.encode(['tuple(bytes message, bytes attestation)'], [data])
84
+ } else if (data?._tag === 'lbtc') {
85
+ return data.attestation as string
86
+ }
87
+ return '0x'
88
+ }
89
+
90
+ /**
91
+ * Try to fetch USDC attestations for transfers, return undefined in position if can't
92
+ *
93
+ * @param tokenAmounts - all tokenAmounts to try
94
+ * @param allLogsInRequest - all other logs in same tx as CCIPSendRequested
95
+ * @param isTestnet - use testnet CCTP API endpoint
96
+ * @returns array where each position is either the attestation for that transfer or undefined
97
+ **/
98
+ async function getUsdcTokenData(
99
+ tokenAmounts: CCIPMessage['tokenAmounts'],
100
+ allLogsInRequest: Pick<Log, 'topics' | 'address' | 'data'>[],
101
+ isTestnet: boolean,
102
+ ): Promise<OffchainTokenData[]> {
103
+ const attestations: OffchainTokenData[] = []
104
+
105
+ const messageSentPerTokenAndPool = allLogsInRequest.reduce((acc, log, i, arr) => {
106
+ // for our MessageSent of interest (USDC-like), the token is the contract
107
+ // which emitted a (burn) Transfer immediately before this event, and the pool emitted a Burned
108
+ // event 2 events after
109
+ const transferLog = arr[i - 1]
110
+ const poolLog = arr[i + 2]
111
+ if (
112
+ log.topics[0] !== USDC_EVENT.topicHash ||
113
+ transferLog?.topics?.[0] !== TRANSFER_EVENT.topicHash ||
114
+ !BURNED_EVENT_TOPIC_HASHES.has(poolLog?.topics?.[0])
115
+ ) {
116
+ return acc
117
+ }
118
+ const token = transferLog.address
119
+ const pool = poolLog.address
120
+ acc.set(token, [...(acc.get(token) ?? []), log])
121
+ acc.set(pool, [...(acc.get(pool) ?? []), log])
122
+ return acc
123
+ }, new Map<string | Addressable, (typeof allLogsInRequest)[number][]>())
124
+
125
+ for (const [i, tokenAmount] of tokenAmounts.entries()) {
126
+ const tokenOrPool = 'token' in tokenAmount ? tokenAmount.token : tokenAmount.sourcePoolAddress
127
+
128
+ // what if there are more USDC transfers of this same token after this one?
129
+ const tokenTransfersCountAfter = tokenAmounts.filter(
130
+ (ta, j) => ('token' in ta ? ta.token : ta.sourcePoolAddress) === tokenOrPool && j > i,
131
+ ).length
132
+
133
+ let messageSentLog: (typeof allLogsInRequest)[number] | undefined
134
+ const messageSents = messageSentPerTokenAndPool.get(tokenOrPool)
135
+ if (messageSents) {
136
+ // look from the end (near our request), but skip MessageSents for further transfers
137
+ messageSentLog = messageSents[messageSents.length - 1 - tokenTransfersCountAfter]
138
+ }
139
+
140
+ let tokenData: OffchainTokenData
141
+ if (messageSentLog) {
142
+ let message
143
+ try {
144
+ message = defaultAbiCoder.decode(USDC_EVENT.inputs, messageSentLog.data)[0] as string
145
+ const attestation = await getUsdcAttestation(message, isTestnet)
146
+ tokenData = {
147
+ _tag: 'usdc',
148
+ message,
149
+ attestation,
150
+ }
151
+ // encoding of OffchainTokenData to be done as part of Chain.executeReceipt
152
+ } catch (err) {
153
+ // maybe not a USDC transfer, or not ready
154
+ console.warn(`❌ EVM CCTP: Failed to fetch attestation for message:`, message, err)
155
+ }
156
+ }
157
+ attestations.push(tokenData)
158
+ }
159
+
160
+ return attestations
161
+ }
162
+
163
+ /**
164
+ * Try to fetch LBTC attestations for transfers, return undefined in position if can't or not required
165
+ **/
166
+ async function getLbtcTokenData(
167
+ tokenAmounts: readonly SourceTokenData[],
168
+ allLogsInRequest: readonly Pick<Log, 'topics' | 'address' | 'data'>[],
169
+ isTestnet: boolean,
170
+ ): Promise<OffchainTokenData[]> {
171
+ const lbtcDepositHashes = new Set(
172
+ allLogsInRequest
173
+ .filter(({ topics }) => LBTC_EVENTS_HASHES.has(topics[0]))
174
+ .map(({ topics }) => topics[3]),
175
+ )
176
+ return Promise.all(
177
+ tokenAmounts.map(async ({ extraData }) => {
178
+ // Attestation is required when SourceTokenData.extraData is 32 bytes long ('0x' + 64 hex chars)
179
+ // otherwise attestation is not required
180
+ if (lbtcDepositHashes.has(extraData)) {
181
+ try {
182
+ return { _tag: 'lbtc', extraData, ...(await getLbtcAttestation(extraData, isTestnet)) }
183
+ } catch (err) {
184
+ console.warn(`❌ EVM LBTC: Failed to fetch attestation for message:`, extraData, err)
185
+ }
186
+ }
187
+ }),
188
+ )
189
+ }
@@ -0,0 +1,131 @@
1
+ import moize from 'moize'
2
+
3
+ import type { Chain, ChainStatic } from './chain.ts'
4
+ import { Tree, getLeafHasher, proofFlagsToBits } from './hasher/index.ts'
5
+ import {
6
+ type CCIPExecution,
7
+ type CCIPMessage,
8
+ type CCIPVersion,
9
+ type CommitReport,
10
+ type ExecutionReport,
11
+ type Lane,
12
+ ExecutionState,
13
+ } from './types.ts'
14
+
15
+ /**
16
+ * Pure/sync function to calculate/generate OffRamp.executeManually report for messageIds
17
+ *
18
+ * @param messagesInBatch - Array containing all messages in batch, ordered
19
+ * @param lane - Arguments for leafeHasher (lane info)
20
+ * @param messageIds - list of messages (from batch) to prove for manual execution
21
+ * @param merkleRoot - Optional merkleRoot of the CommitReport, for validation
22
+ * @returns ManualExec report arguments
23
+ **/
24
+ export function calculateManualExecProof<V extends CCIPVersion = CCIPVersion>(
25
+ messagesInBatch: readonly CCIPMessage<V>[],
26
+ lane: Lane<V>,
27
+ messageId: string,
28
+ merkleRoot?: string,
29
+ ): Omit<ExecutionReport, 'offchainTokenData' | 'message'> {
30
+ const hasher = getLeafHasher(lane)
31
+
32
+ const msgIdx = messagesInBatch.findIndex((message) => message.header.messageId === messageId)
33
+ if (msgIdx < 0) {
34
+ throw new Error(
35
+ `Could not find ${messageId} in batch seqNums=[${Number(messagesInBatch[0].header.sequenceNumber)}..${Number(messagesInBatch[messagesInBatch.length - 1].header.sequenceNumber)}]`,
36
+ )
37
+ }
38
+
39
+ const leaves = messagesInBatch.map((message) => hasher(message))
40
+
41
+ // Create multi-merkle tree
42
+ const tree = new Tree(leaves)
43
+ if (merkleRoot && tree.root() !== merkleRoot) {
44
+ throw new Error(
45
+ `Merkle root created from send events doesn't match ReportAccepted merkle root: expected=${merkleRoot}, got=${tree.root()}`,
46
+ )
47
+ }
48
+
49
+ // Generate proof from multi-merkle tree
50
+ const proof = tree.prove([msgIdx])
51
+
52
+ return {
53
+ proofs: proof.hashes,
54
+ proofFlagBits: proofFlagsToBits(proof.sourceFlags),
55
+ merkleRoot: tree.root(),
56
+ }
57
+ }
58
+
59
+ export const discoverOffRamp = moize.default(
60
+ async function discoverOffRamp_(source: Chain, dest: Chain, onRamp: string): Promise<string> {
61
+ const sourceRouter = await source.getRouterForOnRamp(onRamp, dest.network.chainSelector)
62
+ const sourceOffRamps = await source.getOffRampsForRouter(
63
+ sourceRouter,
64
+ dest.network.chainSelector,
65
+ )
66
+ for (const offRamp of sourceOffRamps) {
67
+ const destOnRamp = await source.getOnRampForOffRamp(offRamp, dest.network.chainSelector)
68
+ const destRouter = await dest.getRouterForOnRamp(destOnRamp, source.network.chainSelector)
69
+ const destOffRamps = await dest.getOffRampsForRouter(destRouter, source.network.chainSelector)
70
+ for (const offRamp of destOffRamps) {
71
+ const offRampsOnRamp = await dest.getOnRampForOffRamp(offRamp, source.network.chainSelector)
72
+ if (offRampsOnRamp === onRamp) {
73
+ console.debug(
74
+ 'discoverOffRamp: found, from',
75
+ { sourceRouter, sourceOffRamps, destOnRamp, destOffRamps, offRampsOnRamp },
76
+ '=',
77
+ offRamp,
78
+ )
79
+ return offRamp
80
+ }
81
+ }
82
+ }
83
+ throw new Error(`No matching offRamp found for "${onRamp}" on "${dest.network.name}"`)
84
+ },
85
+ {
86
+ transformArgs: ([source, dest, onRamp]) => [
87
+ (source as Chain).network.chainSelector,
88
+ (dest as Chain).network.chainSelector,
89
+ onRamp as string,
90
+ ],
91
+ },
92
+ )
93
+
94
+ /**
95
+ * Generic implementation for fetching ExecutionReceipts for given requests
96
+ * If more than one request is given, may yield them interleaved
97
+ * Completes as soon as there's no more work to be done
98
+ * 2 possible behaviors:
99
+ * - if `startBlock|startTime` is given, pages forward from that block up;
100
+ * completes when success (final) receipt is found for all requests (or reach latest block)
101
+ * - otherwise, pages backwards and returns only the most recent receipt per request;
102
+ * completes when receipts for all requests were seen
103
+ *
104
+ * @param dest - provider to page through
105
+ * @param requests - CCIP requests to search executions for
106
+ * @param hints.fromBlock - A block from where to start paging forward;
107
+ * otherwise, page backwards and completes on first (most recent) receipt
108
+ * @param hints.page - getLogs pagination range param
109
+ * @param hints.commit - Special param to help narrow down search on suppported chains (e.g. Solana)
110
+ **/
111
+ export async function* fetchExecutionReceipts(
112
+ dest: Chain,
113
+ offRamp: string,
114
+ messageIds: Set<string>,
115
+ hints?: { startBlock?: number; startTime?: number; page?: number; commit?: CommitReport },
116
+ ): AsyncGenerator<CCIPExecution> {
117
+ const onlyLast = !hints?.startBlock && !hints?.startTime // backwards
118
+ for await (const log of dest.getLogs({
119
+ ...hints,
120
+ address: offRamp,
121
+ topics: ['ExecutionStateChanged'],
122
+ })) {
123
+ const receipt = (dest.constructor as ChainStatic).decodeReceipt(log)
124
+ if (!receipt || !messageIds.has(receipt.messageId)) continue
125
+ if (onlyLast || receipt.state === ExecutionState.Success) messageIds.delete(receipt.messageId)
126
+
127
+ const timestamp = await dest.getBlockTimestamp(log.blockNumber)
128
+ yield { receipt, log, timestamp }
129
+ if (!messageIds.size) break
130
+ }
131
+ }
@@ -0,0 +1,71 @@
1
+ import { type BytesLike, id } from 'ethers'
2
+
3
+ import { ChainFamily } from './chain.ts'
4
+ import { supportedChains } from './supported-chains.ts'
5
+
6
+ export const EVMExtraArgsV1Tag = id('CCIP EVMExtraArgsV1').substring(0, 10) as '0x97a657c9'
7
+ export const EVMExtraArgsV2Tag = id('CCIP EVMExtraArgsV2').substring(0, 10) as '0x181dcf10'
8
+ export const SVMExtraArgsV1Tag = id('CCIP SVMExtraArgsV1').substring(0, 10) as '0x1f3b3aba'
9
+ export const SuiExtraArgsV1Tag = id('CCIP SuiExtraArgsV1').substring(0, 10) as '0x21ea4ca9'
10
+
11
+ export type EVMExtraArgsV1 = {
12
+ gasLimit: bigint
13
+ }
14
+ // aka GenericExtraArgsV2
15
+ export type EVMExtraArgsV2 = EVMExtraArgsV1 & {
16
+ allowOutOfOrderExecution: boolean
17
+ }
18
+ export type SVMExtraArgsV1 = {
19
+ computeUnits: bigint
20
+ accountIsWritableBitmap: bigint
21
+ allowOutOfOrderExecution: boolean
22
+ tokenReceiver: string
23
+ accounts: string[]
24
+ }
25
+ export type SuiExtraArgsV1 = EVMExtraArgsV2 & {
26
+ tokenReceiver: string
27
+ receiverObjectIds: string[]
28
+ }
29
+
30
+ export type ExtraArgs = EVMExtraArgsV1 | EVMExtraArgsV2 | SVMExtraArgsV1 | SuiExtraArgsV1
31
+
32
+ /**
33
+ * Encodes extra arguments for CCIP messages.
34
+ * The args are *to* a dest network, but are encoded as a message *from* this source chain
35
+ * e.g. Solana uses Borsh to encode extraArgs in its produced requests, even those targetting EVM
36
+ **/
37
+ export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily.EVM): string {
38
+ const chain = supportedChains[from]
39
+ if (!chain) throw new Error(`Unsupported chain family: ${from}`)
40
+ return chain.encodeExtraArgs(args)
41
+ }
42
+
43
+ /**
44
+ * Parses extra arguments from CCIP messages
45
+ * @param data - extra arguments bytearray data
46
+ * @returns extra arguments object if found
47
+ **/
48
+ export function decodeExtraArgs(
49
+ data: BytesLike,
50
+ from?: ChainFamily,
51
+ ):
52
+ | (EVMExtraArgsV1 & { _tag: 'EVMExtraArgsV1' })
53
+ | (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
54
+ | (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
55
+ | (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' })
56
+ | undefined {
57
+ if (!data || data === '') return
58
+ let chains
59
+ if (from) {
60
+ const chain = supportedChains[from]
61
+ if (!chain) throw new Error(`Unsupported chain family: ${from}`)
62
+ chains = [chain]
63
+ } else {
64
+ chains = Object.values(supportedChains)
65
+ }
66
+ for (const chain of chains) {
67
+ const decoded = chain.decodeExtraArgs(data)
68
+ if (decoded) return decoded
69
+ }
70
+ throw new Error(`Could not parse extraArgs from "${from}"`)
71
+ }
package/src/gas.ts ADDED
@@ -0,0 +1,135 @@
1
+ import {
2
+ type BytesLike,
3
+ Contract,
4
+ FunctionFragment,
5
+ concat,
6
+ formatUnits,
7
+ getNumber,
8
+ hexlify,
9
+ randomBytes,
10
+ solidityPackedKeccak256,
11
+ toBeHex,
12
+ zeroPadValue,
13
+ } from 'ethers'
14
+ import type { TypedContract } from 'ethers-abitype'
15
+
16
+ import type { Chain } from './chain.ts'
17
+ import TokenABI from './evm/abi/BurnMintERC677Token.ts'
18
+ import RouterABI from './evm/abi/Router.ts'
19
+ import { defaultAbiCoder } from './evm/const.ts'
20
+ import type { EVMChain } from './evm/index.ts'
21
+ import { discoverOffRamp } from './execution.ts'
22
+ import type { Lane } from './types.ts'
23
+
24
+ const BALANCES_SLOT = 0
25
+ const ccipReceive = FunctionFragment.from({
26
+ type: 'function',
27
+ name: 'ccipReceive',
28
+ stateMutability: 'nonpayable',
29
+ inputs: RouterABI.find((v) => v.type === 'function' && v.name === 'routeMessage')!.inputs.slice(
30
+ 0,
31
+ 1,
32
+ ),
33
+ outputs: [],
34
+ })
35
+ type Any2EVMMessage = Parameters<TypedContract<typeof RouterABI>['routeMessage']>[0]
36
+
37
+ /**
38
+ * Estimate CCIP gasLimit needed to execute a request on a contract receiver
39
+ *
40
+ * @param dest - Provider for the destination chain
41
+ * @param request - CCIP request info
42
+ * @param request.lane - Lane info
43
+ * @param request.message - Message info
44
+ * @returns estimated gasLimit as bigint
45
+ **/
46
+ export async function estimateExecGasForRequest(
47
+ source: Chain,
48
+ dest: EVMChain,
49
+ request: {
50
+ lane: Lane
51
+ message: {
52
+ sender: string
53
+ receiver: string
54
+ data: BytesLike
55
+ tokenAmounts: readonly {
56
+ sourcePoolAddress: string
57
+ destTokenAddress: string
58
+ amount: bigint
59
+ }[]
60
+ }
61
+ },
62
+ ) {
63
+ const offRamp = await discoverOffRamp(source, dest, request.lane.onRamp)
64
+ const destRouter = await dest.getRouterForOffRamp(offRamp, request.lane.sourceChainSelector)
65
+
66
+ const destTokenAmounts = await Promise.all(
67
+ request.message.tokenAmounts.map(async (ta) => {
68
+ if (!('destTokenAddress' in ta)) throw new Error('legacy <1.5 tokenPools not supported')
69
+ const [{ decimals: sourceDecimals }, { decimals: destDecimals }] = await Promise.all([
70
+ source
71
+ .getTokenForTokenPool(ta.sourcePoolAddress)
72
+ .then((token) => source.getTokenInfo(token)),
73
+ dest.getTokenInfo(ta.destTokenAddress),
74
+ ])
75
+ const destAmount =
76
+ (ta.amount * 10n ** BigInt(destDecimals - sourceDecimals + 36)) / 10n ** 36n
77
+ if (destAmount === 0n)
78
+ throw new Error(
79
+ `not enough decimals=${destDecimals} for token=${ta.destTokenAddress} on dest=${dest.network.name} to express ${formatUnits(ta.amount, sourceDecimals)}`,
80
+ )
81
+ return { token: ta.destTokenAddress, amount: destAmount }
82
+ }),
83
+ )
84
+
85
+ const message: Any2EVMMessage = {
86
+ messageId: hexlify(randomBytes(32)),
87
+ sender: zeroPadValue(request.message.sender, 32),
88
+ data: hexlify(request.message.data),
89
+ sourceChainSelector: request.lane.sourceChainSelector,
90
+ destTokenAmounts,
91
+ }
92
+
93
+ // we need to override the state, increasing receiver's balance for each token, to simulate the
94
+ // state after tokens were transferred by the offRamp just before calling `ccipReceive`
95
+ const destAmounts: Record<string, bigint> = {}
96
+ const stateOverrides: Record<string, { stateDiff: Record<string, string> }> = {}
97
+ for (const { token, amount } of destTokenAmounts) {
98
+ if (!(token in destAmounts)) {
99
+ const tokenContract = new Contract(token, TokenABI, dest) as unknown as TypedContract<
100
+ typeof TokenABI
101
+ >
102
+ const currentBalance = await tokenContract.balanceOf(request.message.receiver)
103
+ destAmounts[token] = currentBalance
104
+ }
105
+ destAmounts[token] += amount
106
+ stateOverrides[token] = {
107
+ stateDiff: {
108
+ [solidityPackedKeccak256(
109
+ ['uint256', 'uint256'],
110
+ [request.message.receiver, BALANCES_SLOT],
111
+ )]: toBeHex(destAmounts[token], 32),
112
+ },
113
+ }
114
+ }
115
+
116
+ const calldata = concat([
117
+ ccipReceive.selector,
118
+ defaultAbiCoder.encode(ccipReceive.inputs, [message]),
119
+ ])
120
+
121
+ return (
122
+ getNumber(
123
+ (await dest.provider.send('eth_estimateGas', [
124
+ {
125
+ from: destRouter,
126
+ to: request.message.receiver,
127
+ data: calldata,
128
+ },
129
+ 'latest',
130
+ ...(Object.keys(stateOverrides).length ? [stateOverrides] : []),
131
+ ])) as string,
132
+ ) -
133
+ (21_000 - 700) // 21k is the base gas cost for a transaction, 700 is the gas cost of the call
134
+ )
135
+ }
@@ -0,0 +1,23 @@
1
+ import { concat, hexlify, keccak256, toBeHex } from 'ethers'
2
+
3
+ import type { CCIPMessage, CCIPVersion } from '../types.ts'
4
+
5
+ export type LeafHasher<V extends CCIPVersion = CCIPVersion> = (message: CCIPMessage<V>) => string
6
+
7
+ const INTERNAL_DOMAIN_SEPARATOR = toBeHex(1, 32)
8
+ export const LEAF_DOMAIN_SEPARATOR = '0x00'
9
+ export const ZERO_HASH = hexlify(new Uint8Array(32).fill(0xff))
10
+
11
+ /**
12
+ * Computes the Keccak-256 hash of the concatenation of two hash values.
13
+ * @param a The first hash as a Hash type.
14
+ * @param b The second hash as a Hash type.
15
+ * @returns The Keccak-256 hash result as a Hash type.
16
+ */
17
+ export function hashInternal(a: string, b: string): string {
18
+ if (a > b) {
19
+ ;[a, b] = [b, a]
20
+ }
21
+ const combinedData = concat([INTERNAL_DOMAIN_SEPARATOR, a, b])
22
+ return keccak256(combinedData)
23
+ }
@@ -0,0 +1,12 @@
1
+ import { supportedChains } from '../supported-chains.ts'
2
+ import type { CCIPVersion, Lane } from '../types.ts'
3
+ import { networkInfo } from '../utils.ts'
4
+ import type { LeafHasher } from './common.ts'
5
+
6
+ // Factory function that returns the right encoder based on the version of the lane
7
+ export function getLeafHasher<V extends CCIPVersion = CCIPVersion>(lane: Lane<V>): LeafHasher<V> {
8
+ const destFamily = networkInfo(lane.destChainSelector).family
9
+ const chain = supportedChains[destFamily]
10
+ if (!chain) throw new Error(`Unsupported chain family: ${destFamily}`)
11
+ return chain.getDestLeafHasher(lane) as LeafHasher<V>
12
+ }
@@ -0,0 +1,3 @@
1
+ export { type LeafHasher, ZERO_HASH, hashInternal } from './common.ts'
2
+ export { MAX_NUMBER_TREE_LEAVES, Tree, proofFlagsToBits, verifyComputeRoot } from './merklemulti.ts'
3
+ export { getLeafHasher } from './hasher.ts'