@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,1085 @@
1
+ import util from 'util';
2
+ import { AnchorProvider, BorshAccountsCoder, BorshCoder, Program, Wallet, eventDiscriminator, } from '@coral-xyz/anchor';
3
+ import { NATIVE_MINT } from '@solana/spl-token';
4
+ import { Connection, Keypair, PublicKey, SYSVAR_CLOCK_PUBKEY, SystemProgram, } from '@solana/web3.js';
5
+ import bs58 from 'bs58';
6
+ import { concat, dataLength, dataSlice, encodeBase58, encodeBase64, getBytes, hexlify, isHexString, toBigInt, } from 'ethers';
7
+ import moize, {} from 'moize';
8
+ import { Chain, ChainFamily, } from "../chain.js";
9
+ import { EVMExtraArgsV2Tag } from "../extra-args.js";
10
+ import SELECTORS from "../selectors.js";
11
+ import { supportedChains } from "../supported-chains.js";
12
+ import { CCIPVersion, ExecutionState, } from "../types.js";
13
+ import { createRateLimitedFetch, decodeAddress, decodeOnRampAddress, getDataBytes, leToBigInt, networkInfo, parseTypeAndVersion, toLeArray, } from "../utils.js";
14
+ import { cleanUpBuffers } from "./cleanup.js";
15
+ import { executeReport } from "./exec.js";
16
+ import { getV16SolanaLeafHasher } from "./hasher.js";
17
+ import { IDL as BASE_TOKEN_POOL } from "./idl/1.6.0/BASE_TOKEN_POOL.js";
18
+ import { IDL as BURN_MINT_TOKEN_POOL } from "./idl/1.6.0/BURN_MINT_TOKEN_POOL.js";
19
+ import { IDL as CCIP_CCTP_TOKEN_POOL } from "./idl/1.6.0/CCIP_CCTP_TOKEN_POOL.js";
20
+ import { IDL as CCIP_OFFRAMP_IDL } from "./idl/1.6.0/CCIP_OFFRAMP.js";
21
+ import { IDL as CCIP_ROUTER_IDL } from "./idl/1.6.0/CCIP_ROUTER.js";
22
+ import { fetchSolanaOffchainTokenData } from "./offchain.js";
23
+ import { ccipSend, getFee } from "./send.js";
24
+ import { bytesToBuffer, getErrorFromLogs, parseSolanaLogs, simulationProvider } from "./utils.js";
25
+ const routerCoder = new BorshCoder(CCIP_ROUTER_IDL);
26
+ const offrampCoder = new BorshCoder(CCIP_OFFRAMP_IDL);
27
+ const tokenPoolCoder = new BorshCoder({
28
+ ...BURN_MINT_TOKEN_POOL,
29
+ types: BASE_TOKEN_POOL.types,
30
+ events: BASE_TOKEN_POOL.events,
31
+ errors: [...BASE_TOKEN_POOL.errors, ...BURN_MINT_TOKEN_POOL.errors],
32
+ });
33
+ const cctpTokenPoolCoder = new BorshCoder({
34
+ ...CCIP_CCTP_TOKEN_POOL,
35
+ types: [...BASE_TOKEN_POOL.types, ...CCIP_CCTP_TOKEN_POOL.types],
36
+ events: [...BASE_TOKEN_POOL.events, ...CCIP_CCTP_TOKEN_POOL.events],
37
+ errors: [...BASE_TOKEN_POOL.errors, ...CCIP_CCTP_TOKEN_POOL.errors],
38
+ });
39
+ // hardcoded symbols for tokens without metadata
40
+ const unknownTokens = {
41
+ '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU': 'USDC', // devnet
42
+ };
43
+ function hexDiscriminator(eventName) {
44
+ return hexlify(eventDiscriminator(eventName));
45
+ }
46
+ export class SolanaChain extends Chain {
47
+ static family = ChainFamily.Solana;
48
+ static decimals = 9;
49
+ network;
50
+ connection;
51
+ commitment = 'confirmed';
52
+ _getSignaturesForAddress;
53
+ constructor(connection, network) {
54
+ super();
55
+ if (network.family !== ChainFamily.Solana) {
56
+ throw new Error(`Invalid network family for SolanaChain: ${network.family}`);
57
+ }
58
+ this.network = network;
59
+ this.connection = connection;
60
+ // Memoize expensive operations
61
+ this.typeAndVersion = moize.default(this.typeAndVersion.bind(this), {
62
+ maxArgs: 1,
63
+ isPromise: true,
64
+ });
65
+ this.getBlockTimestamp = moize.default(this.getBlockTimestamp.bind(this), {
66
+ isPromise: true,
67
+ maxSize: 100,
68
+ updateCacheForKey: (key) => typeof key[key.length - 1] !== 'number',
69
+ });
70
+ this.getTransaction = moize.default(this.getTransaction.bind(this), {
71
+ maxSize: 100,
72
+ maxArgs: 1,
73
+ });
74
+ this.getWallet = moize.default(this.getWallet.bind(this), { maxSize: 1, maxArgs: 0 });
75
+ this.getTokenForTokenPool = moize.default(this.getTokenForTokenPool.bind(this));
76
+ this.getTokenInfo = moize.default(this.getTokenInfo.bind(this));
77
+ this._getSignaturesForAddress = moize.default((programId, before) => this.connection.getSignaturesForAddress(new PublicKey(programId), { limit: 1000, before }, 'confirmed'), {
78
+ maxSize: 100,
79
+ maxAge: 60000,
80
+ maxArgs: 2,
81
+ isPromise: true,
82
+ updateExpire: true,
83
+ // only expire undefined before (i.e. recent getSignaturesForAddress calls)
84
+ onExpire: ([, before]) => !before,
85
+ });
86
+ // cache account info for 30 seconds
87
+ this.connection.getAccountInfo = moize.default(this.connection.getAccountInfo.bind(this.connection), {
88
+ maxSize: 100,
89
+ maxArgs: 2,
90
+ maxAge: 30e3,
91
+ transformArgs: ([address, commitment]) => [address.toString(), commitment],
92
+ });
93
+ this._getRouterConfig = moize.default(this._getRouterConfig.bind(this), {
94
+ maxArgs: 1,
95
+ });
96
+ this.listFeeTokens = moize.default(this.listFeeTokens.bind(this), {
97
+ maxArgs: 1,
98
+ });
99
+ }
100
+ static _getConnection(url) {
101
+ if (!url.startsWith('http') && !url.startsWith('ws')) {
102
+ throw new Error(`Invalid Solana RPC URL format (should be https://, http://, wss://, or ws://): ${url}`);
103
+ }
104
+ const config = { commitment: 'confirmed' };
105
+ if (url.includes('.solana.com')) {
106
+ config.fetch = createRateLimitedFetch({
107
+ maxRequests: 10,
108
+ maxRetries: 3,
109
+ windowMs: 11e3,
110
+ }); // public nodes
111
+ console.warn('Using rate-limited fetch for public solana nodes, commands may be slow');
112
+ }
113
+ return new Connection(url, config);
114
+ }
115
+ static async fromConnection(connection) {
116
+ // Get genesis hash to use as chainId
117
+ return new SolanaChain(connection, networkInfo(await connection.getGenesisHash()));
118
+ }
119
+ static async fromUrl(url) {
120
+ const connection = this._getConnection(url);
121
+ return this.fromConnection(connection);
122
+ }
123
+ async destroy() {
124
+ // Solana Connection doesn't have an explicit destroy method
125
+ // The memoized functions will be garbage collected when the instance is destroyed
126
+ }
127
+ static getWallet(_opts) {
128
+ throw new Error('Wallet not implemented');
129
+ }
130
+ /**
131
+ * Load wallet
132
+ * @param opts - options to load wallet
133
+ * @param opts.wallet - private key as 0x or base58 string, or async getter function resolving to
134
+ * Wallet instance
135
+ * @returns Wallet, after caching in instance
136
+ */
137
+ async getWallet(opts = {}) {
138
+ try {
139
+ if (typeof opts.wallet === 'string')
140
+ return new Wallet(Keypair.fromSecretKey(opts.wallet.startsWith('0x') ? getBytes(opts.wallet) : bs58.decode(opts.wallet)));
141
+ }
142
+ catch (_) {
143
+ // pass
144
+ }
145
+ return this.constructor.getWallet(opts);
146
+ }
147
+ async getWalletAddress(opts) {
148
+ return (await this.getWallet(opts)).publicKey.toBase58();
149
+ }
150
+ // cached
151
+ async getBlockTimestamp(block) {
152
+ if (block === 'finalized') {
153
+ const slot = await this.connection.getSlot('finalized');
154
+ const blockTime = await this.connection.getBlockTime(slot);
155
+ if (blockTime === null) {
156
+ throw new Error(`Could not get block time for finalized slot ${slot}`);
157
+ }
158
+ return blockTime;
159
+ }
160
+ const blockTime = await this.connection.getBlockTime(block);
161
+ if (blockTime === null) {
162
+ throw new Error(`Could not get block time for slot ${block}`);
163
+ }
164
+ return blockTime;
165
+ }
166
+ // cached
167
+ async getTransaction(hash) {
168
+ const tx = await this.connection.getTransaction(hash, {
169
+ commitment: 'confirmed',
170
+ maxSupportedTransactionVersion: 0,
171
+ });
172
+ if (!tx)
173
+ throw new Error(`Transaction not found: ${hash}`);
174
+ if (tx.blockTime) {
175
+ ;
176
+ this.getBlockTimestamp.set([tx.slot], Promise.resolve(tx.blockTime));
177
+ }
178
+ else {
179
+ tx.blockTime = await this.getBlockTimestamp(tx.slot);
180
+ }
181
+ // Parse logs from transaction using helper function
182
+ const logs_ = tx.meta?.logMessages?.length
183
+ ? parseSolanaLogs(tx.meta?.logMessages).map((l) => ({
184
+ ...l,
185
+ transactionHash: hash,
186
+ blockNumber: tx.slot,
187
+ }))
188
+ : [];
189
+ const chainTx = {
190
+ chain: this,
191
+ hash,
192
+ logs: [],
193
+ blockNumber: tx.slot,
194
+ timestamp: tx.blockTime,
195
+ from: tx.transaction.message.staticAccountKeys[0].toString(),
196
+ error: tx.meta?.err,
197
+ tx, // specialized solana transaction
198
+ };
199
+ // solana logs include circular reference to tx
200
+ chainTx.logs = logs_.map((l) => Object.assign(l, { tx: chainTx }));
201
+ return chainTx;
202
+ }
203
+ // implements inner paging logic for this.getLogs
204
+ async *_getTransactionsForAddress(opts) {
205
+ if (!opts.address)
206
+ throw new Error('Program address is required for Solana log filtering');
207
+ let allSignatures;
208
+ if (opts.startBlock || opts.startTime) {
209
+ // forward collect all matching sigs in array
210
+ const allSigs = [];
211
+ let batch, popped = false;
212
+ while (!popped && (batch?.length ?? true)) {
213
+ batch = await this._getSignaturesForAddress(opts.address, allSigs[allSigs.length - 1]?.signature);
214
+ while (batch.length > 0 &&
215
+ (batch[batch.length - 1].slot < (opts.startBlock || 0) ||
216
+ (batch[batch.length - 1].blockTime || -1) < (opts.startTime || 0))) {
217
+ batch.pop(); // pop tail of txs which are older than requested start
218
+ popped = true;
219
+ }
220
+ allSigs.push(...batch);
221
+ }
222
+ allSigs.reverse();
223
+ while (opts.endBlock &&
224
+ allSigs.length > 0 &&
225
+ allSigs[allSigs.length - 1].slot > opts.endBlock) {
226
+ allSigs.pop(); // pop head (after reverse) of txs which are newer than requested end
227
+ }
228
+ allSignatures = allSigs;
229
+ }
230
+ else {
231
+ allSignatures = async function* () {
232
+ let batch;
233
+ while (batch?.length ?? true) {
234
+ batch = await this._getSignaturesForAddress(opts.address, batch?.length
235
+ ? batch[batch.length - 1].signature
236
+ : opts.endBefore
237
+ ? opts.endBefore
238
+ : undefined);
239
+ for (const sig of batch) {
240
+ if (opts.endBlock && sig.slot > opts.endBlock)
241
+ continue;
242
+ yield sig;
243
+ }
244
+ }
245
+ }.call(this); // generate backwards until depleting getSignaturesForAddress
246
+ }
247
+ // Process signatures
248
+ for await (const signatureInfo of allSignatures) {
249
+ yield await this.getTransaction(signatureInfo.signature);
250
+ }
251
+ }
252
+ /**
253
+ * Retrieves logs from Solana transactions with enhanced chronological ordering.
254
+ *
255
+ * Behavior:
256
+ * - If opts.startBlock or opts.startTime is provided:
257
+ * * Fetches ALL signatures for the address going back in time
258
+ * * Continues fetching until finding signatures older than the start target
259
+ * * Filters out signatures older than start criteria
260
+ * * Returns logs in chronological order (oldest first)
261
+ *
262
+ * - If opts.startBlock and opts.startTime are omitted:
263
+ * * Fetches signatures in reverse chronological order (newest first)
264
+ * * Returns logs in reverse chronological order (newest first)
265
+ *
266
+ * @param opts - Log filter options
267
+ * @param opts.startBlock - Starting slot number (inclusive)
268
+ * @param opts.startTime - Starting Unix timestamp (inclusive)
269
+ * @param opts.endBlock - Ending slot number (inclusive)
270
+ * @param opts.address - Program address to filter logs by (required for Solana)
271
+ * @param opts.topics - Array of topics to filter logs by (optional);
272
+ * either 0x-8B discriminants or event names
273
+ * @param.opts.programs - a special option to allow querying by address of interest, but
274
+ * yielding matching logs from specific (string address) program or any (true)
275
+ * @param opts.commit - Special param for fetching ExecutionReceipts, to narrow down the search
276
+ * @returns AsyncIterableIterator of parsed Log_ objects
277
+ */
278
+ async *getLogs(opts) {
279
+ let programs;
280
+ if (opts.sender && !opts.address) {
281
+ // specialization for fetching txs/requests for a given account of interest without a programID
282
+ opts.address = opts.sender;
283
+ programs = true;
284
+ }
285
+ else if (!opts.address) {
286
+ throw new Error('Program address is required for Solana log filtering');
287
+ }
288
+ else if (!opts.programs) {
289
+ programs = [opts.address];
290
+ }
291
+ else {
292
+ programs = opts.programs;
293
+ }
294
+ if (opts.topics?.length) {
295
+ if (!opts.topics.every((topic) => typeof topic === 'string'))
296
+ throw new Error('Topics must be strings');
297
+ // append events discriminants (if not 0x-8B already), but keep OG topics
298
+ opts.topics.push(...opts.topics.filter((t) => !isHexString(t, 8)).map((t) => hexDiscriminator(t)));
299
+ }
300
+ // Process signatures and yield logs
301
+ for await (const tx of this._getTransactionsForAddress(opts)) {
302
+ for (const log of tx.logs) {
303
+ // Filter and yield logs from the specified program, and which match event discriminant or log prefix
304
+ if ((programs !== true && !programs.includes(log.address)) ||
305
+ (opts.topics?.length &&
306
+ !opts.topics.some((t) => t === log.topics[0] || (typeof log.data === 'string' && log.data.startsWith(t)))))
307
+ continue;
308
+ yield Object.assign(log, { timestamp: new Date(tx.timestamp * 1000) });
309
+ }
310
+ }
311
+ }
312
+ async typeAndVersion(address) {
313
+ const program = new Program(CCIP_OFFRAMP_IDL, // `typeVersion` schema should be the same
314
+ new PublicKey(address), simulationProvider(this.connection));
315
+ // Create the typeVersion instruction
316
+ const returnDataString = (await program.methods
317
+ .typeVersion()
318
+ .accounts({ clock: SYSVAR_CLOCK_PUBKEY })
319
+ .view());
320
+ const res = parseTypeAndVersion(returnDataString.trim());
321
+ if (res[1].startsWith('0.1.'))
322
+ res[1] = CCIPVersion.V1_6;
323
+ return res;
324
+ }
325
+ getRouterForOnRamp(onRamp, _destChainSelector) {
326
+ return Promise.resolve(onRamp); // Solana's router is also the onRamp
327
+ }
328
+ async getRouterForOffRamp(offRamp, _sourceChainSelector) {
329
+ const offRamp_ = new PublicKey(offRamp);
330
+ const program = new Program(CCIP_OFFRAMP_IDL, offRamp_, {
331
+ connection: this.connection,
332
+ });
333
+ const [referenceAddressesAddr] = PublicKey.findProgramAddressSync([Buffer.from('reference_addresses')], offRamp_);
334
+ const referenceAddressesPda = await this.connection.getAccountInfo(referenceAddressesAddr);
335
+ if (!referenceAddressesPda)
336
+ throw new Error(`referenceAddresses account not found for offRamp=${offRamp}`);
337
+ // Decode the config account using the program's coder
338
+ const { router } = program.coder.accounts.decode('referenceAddresses', referenceAddressesPda.data);
339
+ return router.toBase58();
340
+ }
341
+ getNativeTokenForRouter(_router) {
342
+ return Promise.resolve(NATIVE_MINT.toBase58());
343
+ }
344
+ async getOffRampsForRouter(router, sourceChainSelector) {
345
+ // feeQuoter is present in router's config, and has a DestChainState account which is updated by
346
+ // the offramps, so we can use it to narrow the search for the offramp
347
+ const { feeQuoter } = await this._getRouterConfig(router);
348
+ const [feeQuoterDestChainStateAccountAddress] = PublicKey.findProgramAddressSync([Buffer.from('dest_chain'), toLeArray(sourceChainSelector, 8)], feeQuoter);
349
+ for await (const log of this.getLogs({
350
+ programs: true,
351
+ address: feeQuoterDestChainStateAccountAddress.toBase58(),
352
+ topics: ['ExecutionStateChanged', 'CommitReportAccepted', 'Transmitted'],
353
+ })) {
354
+ return [log.address]; // assume single offramp per router/deployment on Solana
355
+ }
356
+ throw new Error(`Could not find OffRamp events in feeQuoter=${feeQuoter.toString()} txs`);
357
+ }
358
+ getOnRampForRouter(router, _destChainSelector) {
359
+ return Promise.resolve(router); // solana's Router is also the OnRamp
360
+ }
361
+ async getOnRampForOffRamp(offRamp, sourceChainSelector) {
362
+ const program = new Program(CCIP_OFFRAMP_IDL, new PublicKey(offRamp), {
363
+ connection: this.connection,
364
+ });
365
+ const [statePda] = PublicKey.findProgramAddressSync([Buffer.from('source_chain_state'), toLeArray(sourceChainSelector, 8)], program.programId);
366
+ // Decode the config account using the program's coder
367
+ const { config: { onRamp }, } = await program.account.sourceChain.fetch(statePda);
368
+ return decodeAddress(new Uint8Array(onRamp.bytes.slice(0, onRamp.len)), networkInfo(sourceChainSelector).family);
369
+ }
370
+ getCommitStoreForOffRamp(offRamp) {
371
+ return Promise.resolve(offRamp); // Solana supports only CCIP>=1.6, for which OffRamp and CommitStore are the same
372
+ }
373
+ async getTokenForTokenPool(tokenPool) {
374
+ const tokenPoolInfo = await this.connection.getAccountInfo(new PublicKey(tokenPool));
375
+ if (!tokenPoolInfo)
376
+ throw new Error(`TokenPool info not found: ${tokenPool}`);
377
+ const { config } = tokenPoolCoder.accounts.decode('state', tokenPoolInfo.data);
378
+ return config.mint.toString();
379
+ }
380
+ async getTokenInfo(token) {
381
+ const mint = new PublicKey(token);
382
+ const mintInfo = await this.connection.getParsedAccountInfo(mint);
383
+ if (!mintInfo.value ||
384
+ !mintInfo.value.data ||
385
+ (typeof mintInfo.value.data === 'object' &&
386
+ 'program' in mintInfo.value.data &&
387
+ mintInfo.value.data.program !== 'spl-token' &&
388
+ mintInfo.value.data.program !== 'spl-token-2022')) {
389
+ throw new Error(`Invalid SPL token or Token-2022: ${token}`);
390
+ }
391
+ if (typeof mintInfo.value.data === 'object' && 'parsed' in mintInfo.value.data) {
392
+ const parsed = mintInfo.value.data.parsed;
393
+ const data = parsed.info;
394
+ let symbol = data.symbol || unknownTokens[token] || 'UNKNOWN';
395
+ let name = data.name;
396
+ // If symbol or name is missing, try to fetch from Metaplex metadata
397
+ if (!data.symbol || symbol === 'UNKNOWN' || !data.name) {
398
+ try {
399
+ const metadata = await this._fetchTokenMetadata(mint);
400
+ if (metadata) {
401
+ if (metadata.symbol && (!data.symbol || symbol === 'UNKNOWN')) {
402
+ symbol = metadata.symbol;
403
+ }
404
+ if (metadata.name && !name) {
405
+ name = metadata.name;
406
+ }
407
+ }
408
+ }
409
+ catch (error) {
410
+ // Metaplex metadata fetch failed, keep the default values
411
+ console.debug(`Failed to fetch Metaplex metadata for token ${token}:`, error);
412
+ }
413
+ }
414
+ return {
415
+ name,
416
+ symbol,
417
+ decimals: data.decimals,
418
+ };
419
+ }
420
+ else {
421
+ throw new Error(`Unable to parse token data for ${token}`);
422
+ }
423
+ }
424
+ async _fetchTokenMetadata(mintPublicKey) {
425
+ try {
426
+ // Token Metadata Program ID
427
+ const TOKEN_METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
428
+ // Derive metadata account address
429
+ const [metadataPDA] = PublicKey.findProgramAddressSync([Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mintPublicKey.toBuffer()], TOKEN_METADATA_PROGRAM_ID);
430
+ // Fetch metadata account
431
+ const metadataAccount = await this.connection.getAccountInfo(metadataPDA);
432
+ if (!metadataAccount) {
433
+ return null;
434
+ }
435
+ // Parse Metaplex Token Metadata according to the actual format
436
+ // Reference: https://docs.metaplex.com/programs/token-metadata/accounts#metadata
437
+ const data = metadataAccount.data;
438
+ if (data.length < 100) {
439
+ return null;
440
+ }
441
+ let offset = 0;
442
+ // Skip key (1 byte) - discriminator for account type
443
+ offset += 1;
444
+ // Skip update_authority (32 bytes)
445
+ offset += 32;
446
+ // Skip mint (32 bytes)
447
+ offset += 32;
448
+ // Parse name (variable length string)
449
+ if (offset + 4 > data.length)
450
+ return null;
451
+ const nameLength = data.readUInt32LE(offset);
452
+ offset += 4;
453
+ if (nameLength > 200 || offset + nameLength > data.length)
454
+ return null;
455
+ const nameBytes = data.subarray(offset, offset + nameLength);
456
+ const name = nameBytes.toString('utf8').replace(/\0/g, '').trim();
457
+ offset += nameLength;
458
+ // Parse symbol (variable length string)
459
+ if (offset + 4 > data.length)
460
+ return null;
461
+ const symbolLength = data.readUInt32LE(offset);
462
+ offset += 4;
463
+ if (symbolLength > 50 || offset + symbolLength > data.length)
464
+ return null;
465
+ const symbolBytes = data.subarray(offset, offset + symbolLength);
466
+ const symbol = symbolBytes.toString('utf8').replace(/\0/g, '').trim();
467
+ return name || symbol ? { name, symbol } : null;
468
+ }
469
+ catch (error) {
470
+ console.debug('Error fetching token metadata:', error);
471
+ return null;
472
+ }
473
+ }
474
+ static decodeMessage({ data }) {
475
+ if (!data || typeof data !== 'string')
476
+ return undefined;
477
+ let eventDataBuffer;
478
+ try {
479
+ eventDataBuffer = bytesToBuffer(data);
480
+ }
481
+ catch (_) {
482
+ return;
483
+ }
484
+ const disc = dataSlice(eventDataBuffer, 0, 8);
485
+ if (disc !== hexDiscriminator('CCIPMessageSent'))
486
+ return;
487
+ // Use module-level BorshCoder for decoding structs
488
+ // Manually parse event header (discriminator + event-level fields)
489
+ let offset = 8;
490
+ // Parse event-level fields
491
+ const _destChainSelector = eventDataBuffer.readBigUInt64LE(offset);
492
+ offset += 8;
493
+ const _sequenceNumber = eventDataBuffer.readBigUInt64LE(offset);
494
+ offset += 8;
495
+ // Now decode the SVM2AnyRampMessage struct using BorshCoder
496
+ const messageBytes = eventDataBuffer.subarray(offset);
497
+ const message = routerCoder.types.decode('SVM2AnyRampMessage', messageBytes);
498
+ // Convert BN/number types to bigints
499
+ const sourceChainSelector = BigInt(message.header.sourceChainSelector.toString());
500
+ const destChainSelector = BigInt(message.header.destChainSelector.toString());
501
+ const sequenceNumber = BigInt(message.header.sequenceNumber.toString());
502
+ const nonce = BigInt(message.header.nonce.toString());
503
+ const destNetwork = networkInfo(destChainSelector);
504
+ // Convert message fields to expected format
505
+ const messageId = hexlify(new Uint8Array(message.header.messageId));
506
+ const sender = message.sender.toString();
507
+ const data_ = getDataBytes(message.data);
508
+ // TODO: extract this into a proper normalize/decode/reencode data utility
509
+ const msgData = destNetwork.family === ChainFamily.Solana ? encodeBase64(data_) : hexlify(data_);
510
+ const receiver = decodeAddress(message.receiver, destNetwork.family);
511
+ const feeToken = message.feeToken.toString();
512
+ // Process token amounts
513
+ const tokenAmounts = message.tokenAmounts.map((ta) => ({
514
+ sourcePoolAddress: ta.sourcePoolAddress.toBase58(),
515
+ destTokenAddress: decodeAddress(ta.destTokenAddress, destNetwork.family),
516
+ extraData: hexlify(ta.extraData),
517
+ amount: leToBigInt(ta.amount.leBytes),
518
+ destExecData: hexlify(ta.destExecData),
519
+ // destGasAmount is encoded as BE uint32;
520
+ destGasAmount: toBigInt(ta.destExecData),
521
+ }));
522
+ // Convert fee amounts from CrossChainAmount format
523
+ const feeTokenAmount = leToBigInt(message.feeTokenAmount.leBytes);
524
+ const feeValueJuels = leToBigInt(message.feeValueJuels.leBytes);
525
+ // Parse gas limit from extraArgs
526
+ const extraArgs = hexlify(message.extraArgs);
527
+ const parsed = this.decodeExtraArgs(extraArgs);
528
+ if (!parsed)
529
+ throw new Error('Invalid extraArgs: ' + extraArgs);
530
+ const { _tag, ...rest } = parsed;
531
+ return {
532
+ header: {
533
+ messageId,
534
+ sourceChainSelector,
535
+ destChainSelector: destChainSelector,
536
+ sequenceNumber: sequenceNumber,
537
+ nonce,
538
+ },
539
+ sender,
540
+ receiver,
541
+ data: msgData,
542
+ tokenAmounts,
543
+ feeToken,
544
+ feeTokenAmount,
545
+ feeValueJuels,
546
+ extraArgs,
547
+ ...rest,
548
+ };
549
+ }
550
+ static decodeExtraArgs(extraArgs) {
551
+ const data = getDataBytes(extraArgs), tag = dataSlice(data, 0, 4);
552
+ switch (tag) {
553
+ case EVMExtraArgsV2Tag: {
554
+ if (dataLength(data) === 4 + 16 + 1) {
555
+ // Solana-generated EVMExtraArgsV2 (21 bytes total)
556
+ return {
557
+ _tag: 'EVMExtraArgsV2',
558
+ gasLimit: leToBigInt(dataSlice(data, 4, 4 + 16)), // from Uint128LE
559
+ allowOutOfOrderExecution: data[4 + 16] == 1,
560
+ };
561
+ }
562
+ throw new Error(`Unsupported EVMExtraArgsV2 length: ${dataLength(data)}`);
563
+ }
564
+ default:
565
+ return;
566
+ }
567
+ }
568
+ static encodeExtraArgs(args) {
569
+ if ('computeUnits' in args)
570
+ throw new Error('Solana can only encode EVMExtraArgsV2');
571
+ const gasLimitUint128Le = toLeArray(args.gasLimit, 16);
572
+ return concat([
573
+ EVMExtraArgsV2Tag,
574
+ gasLimitUint128Le,
575
+ 'allowOutOfOrderExecution' in args && args.allowOutOfOrderExecution ? '0x01' : '0x00',
576
+ ]);
577
+ }
578
+ static decodeCommits(log, lane) {
579
+ // Check if this is a CommitReportAccepted event by looking at the discriminant
580
+ if (!log.data || typeof log.data !== 'string') {
581
+ throw new Error('Log data is missing or not a string');
582
+ }
583
+ const eventDataBuffer = bytesToBuffer(log.data);
584
+ // Verify the discriminant matches CommitReportAccepted
585
+ const expectedDiscriminant = hexDiscriminator('CommitReportAccepted');
586
+ const actualDiscriminant = hexlify(eventDataBuffer.subarray(0, 8));
587
+ if (actualDiscriminant !== expectedDiscriminant)
588
+ return;
589
+ // Skip the 8-byte discriminant and decode the event data manually
590
+ let offset = 8;
591
+ // Decode Option<MerkleRoot> - first byte indicates Some(1) or None(0)
592
+ const hasValue = eventDataBuffer.readUInt8(offset);
593
+ offset += 1;
594
+ if (!hasValue)
595
+ return [];
596
+ // Decode MerkleRoot struct using the types decoder
597
+ // We need to read the remaining bytes as a MerkleRoot struct
598
+ const merkleRootBytes = eventDataBuffer.subarray(offset);
599
+ const merkleRootData = offrampCoder.types.decode('MerkleRoot', merkleRootBytes);
600
+ if (!merkleRootData) {
601
+ throw new Error('Failed to decode MerkleRoot data');
602
+ }
603
+ // Verify the source chain selector matches our lane
604
+ const sourceChainSelector = BigInt(merkleRootData.sourceChainSelector.toString());
605
+ // Convert the onRampAddress from bytes to the proper format
606
+ const onRampAddress = decodeOnRampAddress(merkleRootData.onRampAddress, networkInfo(sourceChainSelector).family);
607
+ if (lane) {
608
+ if (sourceChainSelector !== lane.sourceChainSelector)
609
+ return;
610
+ // Verify the onRampAddress matches our lane
611
+ if (onRampAddress !== lane.onRamp)
612
+ return;
613
+ }
614
+ return [
615
+ {
616
+ sourceChainSelector,
617
+ onRampAddress,
618
+ minSeqNr: BigInt(merkleRootData.minSeqNr.toString()),
619
+ maxSeqNr: BigInt(merkleRootData.maxSeqNr.toString()),
620
+ merkleRoot: hexlify(new Uint8Array(merkleRootData.merkleRoot)),
621
+ },
622
+ ];
623
+ }
624
+ static decodeReceipt(log) {
625
+ // Check if this is a ExecutionStateChanged event by looking at the discriminant
626
+ if (!log.data || typeof log.data !== 'string') {
627
+ throw new Error('Log data is missing or not a string');
628
+ }
629
+ // Verify the discriminant matches ExecutionStateChanged
630
+ if (dataSlice(getDataBytes(log.data), 0, 8) !== hexDiscriminator('ExecutionStateChanged'))
631
+ return;
632
+ const eventDataBuffer = bytesToBuffer(log.data);
633
+ // Note: We manually decode the event fields rather than using BorshCoder
634
+ // since ExecutionStateChanged is an event, not a defined type
635
+ // Skip the 8-byte discriminant and manually decode the event fields
636
+ let offset = 8;
637
+ // Decode sourceChainSelector (u64)
638
+ const sourceChainSelector = eventDataBuffer.readBigUInt64LE(offset);
639
+ offset += 8;
640
+ // Decode sequenceNumber (u64)
641
+ const sequenceNumber = eventDataBuffer.readBigUInt64LE(offset);
642
+ offset += 8;
643
+ // Decode messageId ([u8; 32])
644
+ const messageId = hexlify(eventDataBuffer.subarray(offset, offset + 32));
645
+ offset += 32;
646
+ // Decode messageHash ([u8; 32])
647
+ const messageHash = hexlify(eventDataBuffer.subarray(offset, offset + 32));
648
+ offset += 32;
649
+ // Decode state enum (MessageExecutionState)
650
+ // Enum discriminant is a single byte: Untouched=0, InProgress=1, Success=2, Failure=3
651
+ let state = eventDataBuffer.readUInt8(offset);
652
+ let returnData;
653
+ if (log.tx) {
654
+ // use only last receipt per tx+message (i.e. skip intermediary InProgress=1 states for Solana)
655
+ const laterReceiptLog = log.tx.logs
656
+ .filter((l) => l.index > log.index)
657
+ .findLast((l) => {
658
+ const lastReceipt = this.decodeReceipt(l);
659
+ return lastReceipt && lastReceipt.messageId === messageId;
660
+ });
661
+ if (laterReceiptLog) {
662
+ return; // ignore intermediary state (InProgress=1) if we can find a later receipt
663
+ }
664
+ else if (state !== ExecutionState.Success) {
665
+ returnData = getErrorFromLogs(log.tx.logs);
666
+ }
667
+ else if (log.tx.error) {
668
+ returnData = util.inspect(log.tx.error);
669
+ state = ExecutionState.Failed;
670
+ }
671
+ }
672
+ return {
673
+ sourceChainSelector,
674
+ sequenceNumber,
675
+ messageId,
676
+ messageHash,
677
+ state,
678
+ returnData,
679
+ };
680
+ }
681
+ static getAddress(bytes) {
682
+ try {
683
+ if (typeof bytes === 'string' && bs58.decode(bytes).length === 32)
684
+ return bytes;
685
+ }
686
+ catch (_) {
687
+ // pass
688
+ }
689
+ return encodeBase58(getDataBytes(bytes));
690
+ }
691
+ static getDestLeafHasher(lane) {
692
+ return getV16SolanaLeafHasher(lane);
693
+ }
694
+ async getTokenAdminRegistryFor(address) {
695
+ const [type] = await this.typeAndVersion(address);
696
+ if (!type.includes('Router'))
697
+ throw new Error(`Not a Router: ${address} is ${type}`);
698
+ // Solana implements TokenAdminRegistry in the Router/OnRamp program
699
+ return address;
700
+ }
701
+ /**
702
+ * Get the fee required to send a CCIP message from the Solana router.
703
+ */
704
+ getFee(router, destChainSelector, message) {
705
+ return getFee(this.connection, router, destChainSelector, message);
706
+ }
707
+ async sendMessage(router_, destChainSelector, message, opts) {
708
+ const wallet = await this.getWallet(opts);
709
+ const router = new Program(CCIP_ROUTER_IDL, new PublicKey(router_), new AnchorProvider(this.connection, wallet, { commitment: this.commitment }));
710
+ const { hash } = await ccipSend(router, destChainSelector, message, opts);
711
+ return this.getTransaction(hash);
712
+ }
713
+ async fetchOffchainTokenData(request) {
714
+ return fetchSolanaOffchainTokenData(this.connection, request);
715
+ }
716
+ async executeReport(offRamp, execReport_, opts) {
717
+ if (!('computeUnits' in execReport_.message))
718
+ throw new Error("ExecutionReport's message not for Solana");
719
+ const execReport = execReport_;
720
+ const wallet = await this.getWallet(opts);
721
+ const provider = new AnchorProvider(this.connection, wallet, { commitment: this.commitment });
722
+ const offrampProgram = new Program(CCIP_OFFRAMP_IDL, new PublicKey(offRamp), provider);
723
+ const rep = await executeReport({ offrampProgram, execReport, ...opts });
724
+ if (opts?.clearLeftoverAccounts) {
725
+ try {
726
+ await this.cleanUpBuffers(opts);
727
+ }
728
+ catch (err) {
729
+ console.warn('Error while trying to clean up buffers:', err);
730
+ }
731
+ }
732
+ return this.getTransaction(rep.hash);
733
+ }
734
+ /**
735
+ * Clean up and recycle buffers and address lookup tables owned by wallet
736
+ * CAUTION: this will close ANY lookup table owned by this wallet
737
+ * @param wallet - wallet options
738
+ * @param dontWait - Whether to skip waiting for lookup table deactivation cool down period
739
+ * (513 slots) to pass before closing; by default, we deactivate (if needed) and wait to close
740
+ * before returning from this method
741
+ */
742
+ async cleanUpBuffers(opts) {
743
+ const wallet = await this.getWallet(opts);
744
+ const provider = new AnchorProvider(this.connection, wallet, { commitment: this.commitment });
745
+ await cleanUpBuffers(provider, this.getLogs.bind(this), opts);
746
+ }
747
+ static parse(data) {
748
+ if (!data)
749
+ return;
750
+ try {
751
+ if (Array.isArray(data)) {
752
+ if (data.every((e) => typeof e === 'string'))
753
+ return getErrorFromLogs(data);
754
+ else if (data.every((e) => typeof e === 'object' && 'data' in e && 'address' in e))
755
+ return getErrorFromLogs(data);
756
+ }
757
+ else if (typeof data === 'object') {
758
+ if ('transactionLogs' in data && 'transactionMessage' in data) {
759
+ const parsed = getErrorFromLogs(data.transactionLogs);
760
+ if (parsed)
761
+ return { message: data.transactionMessage, ...parsed };
762
+ }
763
+ if ('logs' in data)
764
+ return getErrorFromLogs(data.logs);
765
+ }
766
+ else if (typeof data === 'string') {
767
+ const parsedExtraArgs = this.decodeExtraArgs(getDataBytes(data));
768
+ if (parsedExtraArgs)
769
+ return parsedExtraArgs;
770
+ const parsedMessage = this.decodeMessage({ data });
771
+ if (parsedMessage)
772
+ return parsedMessage;
773
+ }
774
+ }
775
+ catch (_) {
776
+ // Ignore errors during parsing
777
+ }
778
+ }
779
+ /**
780
+ * Solana optimization: we use getProgramAccounts with
781
+ */
782
+ async fetchCommitReport(commitStore, request, hints) {
783
+ const commitsAroundSeqNum = await this.connection.getProgramAccounts(new PublicKey(commitStore), {
784
+ filters: [
785
+ {
786
+ memcmp: {
787
+ offset: 0,
788
+ bytes: encodeBase58(BorshAccountsCoder.accountDiscriminator('CommitReport')),
789
+ },
790
+ },
791
+ {
792
+ memcmp: {
793
+ offset: 8 + 1,
794
+ bytes: encodeBase58(toLeArray(request.lane.sourceChainSelector, 8)),
795
+ },
796
+ },
797
+ // dirty trick: memcmp report.min with msg.sequenceNumber's without least-significant byte;
798
+ // this should be ~256 around seqNum, i.e. big chance of a match
799
+ {
800
+ memcmp: {
801
+ offset: 8 + 1 + 8 + 32 + 8 + 1,
802
+ bytes: encodeBase58(toLeArray(request.message.header.sequenceNumber, 8).slice(1)),
803
+ },
804
+ },
805
+ ],
806
+ });
807
+ for (const acc of commitsAroundSeqNum) {
808
+ // const merkleRoot = acc.account.data.subarray(8 + 1 + 8, 8 + 1 + 8 + 32)
809
+ const minSeqNr = acc.account.data.readBigUInt64LE(8 + 1 + 8 + 32 + 8);
810
+ const maxSeqNr = acc.account.data.readBigUInt64LE(8 + 1 + 8 + 32 + 8 + 8);
811
+ if (minSeqNr > request.message.header.sequenceNumber ||
812
+ maxSeqNr < request.message.header.sequenceNumber)
813
+ continue;
814
+ // we have all the commit report info, but we also need log details (txHash, etc)
815
+ for await (const log of this.getLogs({
816
+ startTime: 1, // just to force getting the oldest log first
817
+ programs: [commitStore],
818
+ address: acc.pubkey.toBase58(),
819
+ topics: ['CommitReportAccepted'],
820
+ })) {
821
+ // first yielded log should be commit (which created this PDA)
822
+ const report = this.constructor.decodeCommits(log, request.lane)?.[0];
823
+ if (report)
824
+ return { report, log };
825
+ }
826
+ }
827
+ // in case we can't find it, fallback to generic iterating txs
828
+ return super.fetchCommitReport(commitStore, request, hints);
829
+ }
830
+ // specialized override with stricter address-of-interest
831
+ async *fetchExecutionReceipts(offRamp, messageIds, hints) {
832
+ if (!hints?.commit) {
833
+ // if no commit, fall back to generic implementation
834
+ yield* super.fetchExecutionReceipts(offRamp, messageIds, hints);
835
+ return;
836
+ }
837
+ // otherwise, use `commit_report` PDA as more specialized address
838
+ const [commitReportPda] = PublicKey.findProgramAddressSync([
839
+ Buffer.from('commit_report'),
840
+ toLeArray(hints.commit.sourceChainSelector, 8),
841
+ bytesToBuffer(hints.commit.merkleRoot),
842
+ ], new PublicKey(offRamp));
843
+ // rest is similar to generic implemenetation
844
+ const onlyLast = !hints?.startBlock && !hints?.startTime; // backwards
845
+ for await (const log of this.getLogs({
846
+ ...hints,
847
+ programs: [offRamp],
848
+ address: commitReportPda.toBase58(),
849
+ topics: ['ExecutionStateChanged'],
850
+ })) {
851
+ const receipt = this.constructor.decodeReceipt(log);
852
+ if (!receipt || !messageIds.has(receipt.messageId))
853
+ continue;
854
+ if (onlyLast || receipt.state === ExecutionState.Success)
855
+ messageIds.delete(receipt.messageId);
856
+ const timestamp = await this.getBlockTimestamp(log.blockNumber);
857
+ yield { receipt, log, timestamp };
858
+ if (!messageIds.size)
859
+ break;
860
+ }
861
+ }
862
+ async getRegistryTokenConfig(registry, token) {
863
+ const registry_ = new PublicKey(registry);
864
+ const tokenMint = new PublicKey(token);
865
+ const [tokenAdminRegistryAddr] = PublicKey.findProgramAddressSync([Buffer.from('token_admin_registry'), tokenMint.toBuffer()], registry_);
866
+ const tokenAdminRegistry = await this.connection.getAccountInfo(tokenAdminRegistryAddr);
867
+ if (!tokenAdminRegistry)
868
+ throw new Error(`Token ${token} is not configured in registry ${registry}`);
869
+ const config = {
870
+ administrator: encodeBase58(tokenAdminRegistry.data.subarray(9, 9 + 32)),
871
+ };
872
+ const pendingAdministrator = new PublicKey(tokenAdminRegistry.data.subarray(41, 41 + 32));
873
+ // Check if pendingAdministrator is set (not system program address)
874
+ if (pendingAdministrator &&
875
+ !pendingAdministrator.equals(SystemProgram.programId) &&
876
+ !pendingAdministrator.equals(PublicKey.default)) {
877
+ config.pendingAdministrator = pendingAdministrator.toBase58();
878
+ }
879
+ // Get token pool from lookup table if available
880
+ try {
881
+ const lookupTableAddr = new PublicKey(tokenAdminRegistry.data.subarray(73, 73 + 32));
882
+ const lookupTable = await this.connection.getAddressLookupTable(lookupTableAddr);
883
+ if (lookupTable?.value) {
884
+ // tokenPool state PDA is at index [3]
885
+ const tokenPoolAddress = lookupTable.value.state.addresses[3];
886
+ if (tokenPoolAddress && !tokenPoolAddress.equals(PublicKey.default)) {
887
+ config.tokenPool = tokenPoolAddress.toBase58();
888
+ }
889
+ }
890
+ }
891
+ catch (_err) {
892
+ // Token pool may not be configured yet
893
+ }
894
+ return config;
895
+ }
896
+ async getTokenPoolConfigs(tokenPool) {
897
+ // `tokenPool` is actually a State PDA in the tokenPoolProgram
898
+ const tokenPoolState = await this.connection.getAccountInfo(new PublicKey(tokenPool));
899
+ if (!tokenPoolState)
900
+ throw new Error(`TokenPool State PDA not found at ${tokenPool}`);
901
+ const { config } = tokenPoolCoder.accounts.decode('state', tokenPoolState.data);
902
+ const tokenPoolProgram = tokenPoolState.owner.toBase58();
903
+ let typeAndVersion;
904
+ try {
905
+ ;
906
+ [, , typeAndVersion] = await this.typeAndVersion(tokenPoolProgram);
907
+ }
908
+ catch (_) {
909
+ // TokenPool may not have a typeAndVersion
910
+ }
911
+ return {
912
+ token: config.mint.toBase58(),
913
+ router: config.router.toBase58(),
914
+ tokenPoolProgram,
915
+ typeAndVersion,
916
+ };
917
+ }
918
+ async getTokenPoolRemotes(tokenPool, remoteChainSelector) {
919
+ // `tokenPool` is actually a State PDA in the tokenPoolProgram
920
+ const tokenPoolState = await this.connection.getAccountInfo(new PublicKey(tokenPool));
921
+ if (!tokenPoolState)
922
+ throw new Error(`TokenPool State PDA not found at ${tokenPool}`);
923
+ const tokenPoolProgram = tokenPoolState.owner;
924
+ const { config } = tokenPoolCoder.accounts.decode('state', tokenPoolState.data);
925
+ // Get all supported chains by fetching ChainConfig PDAs
926
+ // We need to scan for all ChainConfig accounts owned by this token pool program
927
+ const remotes = {};
928
+ // Fetch all ChainConfig accounts for this token pool
929
+ let selectors = Object.values(SELECTORS);
930
+ let accounts;
931
+ if (remoteChainSelector) {
932
+ selectors = [{ selector: remoteChainSelector }];
933
+ const [chainConfigAddr] = PublicKey.findProgramAddressSync([
934
+ Buffer.from('ccip_tokenpool_chainconfig'),
935
+ toLeArray(remoteChainSelector, 8),
936
+ config.mint.toBuffer(),
937
+ ], tokenPoolProgram);
938
+ const chainConfigAcc = await this.connection.getAccountInfo(chainConfigAddr);
939
+ if (!chainConfigAcc)
940
+ throw new Error(`ChainConfig not found at ${chainConfigAddr.toBase58()} for tokenPool=${tokenPool} and remoteNetwork=${networkInfo(remoteChainSelector).name}`);
941
+ accounts = [
942
+ {
943
+ pubkey: chainConfigAddr,
944
+ account: chainConfigAcc,
945
+ },
946
+ ];
947
+ }
948
+ else
949
+ accounts = await this.connection.getProgramAccounts(tokenPoolProgram, {
950
+ filters: [
951
+ {
952
+ memcmp: {
953
+ offset: 0,
954
+ bytes: encodeBase58(BorshAccountsCoder.accountDiscriminator('ChainConfig')),
955
+ },
956
+ },
957
+ ],
958
+ });
959
+ for (const acc of accounts) {
960
+ try {
961
+ let base;
962
+ try {
963
+ ;
964
+ ({ base } = tokenPoolCoder.accounts.decode('chainConfig', acc.account.data));
965
+ }
966
+ catch (_) {
967
+ ;
968
+ ({ base } = cctpTokenPoolCoder.accounts.decode('chainConfig', acc.account.data));
969
+ }
970
+ let remoteChainSelector;
971
+ // test all selectors, to find the correct seed
972
+ for (const { selector } of Object.values(selectors)) {
973
+ const [chainConfigAddr] = PublicKey.findProgramAddressSync([
974
+ Buffer.from('ccip_tokenpool_chainconfig'),
975
+ toLeArray(selector, 8),
976
+ config.mint.toBuffer(),
977
+ ], tokenPoolProgram);
978
+ if (chainConfigAddr.equals(acc.pubkey)) {
979
+ remoteChainSelector = selector;
980
+ break;
981
+ }
982
+ }
983
+ if (!remoteChainSelector)
984
+ continue;
985
+ const remoteNetwork = networkInfo(remoteChainSelector);
986
+ const remoteToken = decodeAddress(base.remote.tokenAddress.address, remoteNetwork.family);
987
+ const remotePools = base.remote.poolAddresses.map((pool) => decodeAddress(pool.address, remoteNetwork.family));
988
+ let inboundRateLimiterState = null;
989
+ if (base.inboundRateLimit.cfg.enabled) {
990
+ inboundRateLimiterState = {
991
+ tokens: BigInt(base.inboundRateLimit.tokens.toString()),
992
+ capacity: BigInt(base.inboundRateLimit.cfg.capacity.toString()),
993
+ rate: BigInt(base.inboundRateLimit.cfg.rate.toString()),
994
+ };
995
+ const cur = inboundRateLimiterState.tokens +
996
+ inboundRateLimiterState.rate *
997
+ BigInt(Math.floor(Date.now() / 1000) - base.inboundRateLimit.lastUpdated.toNumber());
998
+ if (cur < inboundRateLimiterState.capacity)
999
+ inboundRateLimiterState.tokens = cur;
1000
+ else
1001
+ inboundRateLimiterState.tokens = inboundRateLimiterState.capacity;
1002
+ }
1003
+ let outboundRateLimiterState = null;
1004
+ if (base.outboundRateLimit.cfg.enabled) {
1005
+ outboundRateLimiterState = {
1006
+ tokens: BigInt(base.outboundRateLimit.tokens.toString()),
1007
+ capacity: BigInt(base.outboundRateLimit.cfg.capacity.toString()),
1008
+ rate: BigInt(base.outboundRateLimit.cfg.rate.toString()),
1009
+ };
1010
+ const cur = outboundRateLimiterState.tokens +
1011
+ outboundRateLimiterState.rate *
1012
+ BigInt(Math.floor(Date.now() / 1000) - base.outboundRateLimit.lastUpdated.toNumber());
1013
+ if (cur < outboundRateLimiterState.capacity)
1014
+ outboundRateLimiterState.tokens = cur;
1015
+ else
1016
+ outboundRateLimiterState.tokens = outboundRateLimiterState.capacity;
1017
+ }
1018
+ remotes[remoteNetwork.name] = {
1019
+ remoteToken,
1020
+ remotePools,
1021
+ inboundRateLimiterState,
1022
+ outboundRateLimiterState,
1023
+ };
1024
+ }
1025
+ catch (err) {
1026
+ console.warn('Failed to decode ChainConfig account:', err);
1027
+ }
1028
+ }
1029
+ return remotes;
1030
+ }
1031
+ async getSupportedTokens(router) {
1032
+ // `mint` offset in TokenAdminRegistry account data; more robust against changes in layout
1033
+ const mintOffset = 8 + 1 + 32 + 32 + 32 + 16 * 2; // = 137
1034
+ const router_ = new PublicKey(router);
1035
+ const res = [];
1036
+ for (const acc of await this.connection.getProgramAccounts(router_, {
1037
+ filters: [
1038
+ {
1039
+ memcmp: {
1040
+ offset: 0,
1041
+ bytes: encodeBase58(BorshAccountsCoder.accountDiscriminator('TokenAdminRegistry')),
1042
+ },
1043
+ },
1044
+ ],
1045
+ })) {
1046
+ if (!acc.account.data || acc.account.data.length < mintOffset + 32)
1047
+ continue;
1048
+ const mint = new PublicKey(acc.account.data.subarray(mintOffset, mintOffset + 32));
1049
+ const [derivedPda] = PublicKey.findProgramAddressSync([Buffer.from('token_admin_registry'), mint.toBuffer()], router_);
1050
+ if (!acc.pubkey.equals(derivedPda))
1051
+ continue;
1052
+ res.push(mint.toBase58());
1053
+ }
1054
+ return res;
1055
+ }
1056
+ async listFeeTokens(router) {
1057
+ const { feeQuoter } = await this._getRouterConfig(router);
1058
+ const tokenConfigs = await this.connection.getProgramAccounts(feeQuoter, {
1059
+ filters: [
1060
+ {
1061
+ memcmp: {
1062
+ offset: 0,
1063
+ bytes: encodeBase58(BorshAccountsCoder.accountDiscriminator('BillingTokenConfigWrapper')),
1064
+ },
1065
+ },
1066
+ ],
1067
+ });
1068
+ return Object.fromEntries(await Promise.all(tokenConfigs.map(async (acc) => {
1069
+ const token = new PublicKey(acc.account.data.subarray(10, 10 + 32));
1070
+ return [token.toBase58(), await this.getTokenInfo(token.toBase58())];
1071
+ })));
1072
+ }
1073
+ // cached
1074
+ async _getRouterConfig(router) {
1075
+ const program = new Program(CCIP_ROUTER_IDL, new PublicKey(router), {
1076
+ connection: this.connection,
1077
+ });
1078
+ const [configPda] = PublicKey.findProgramAddressSync([Buffer.from('config')], program.programId);
1079
+ // feeQuoter is present in router's config, and has a DestChainState account which is updated by
1080
+ // the offramps, so we can use it to narrow the search for the offramp
1081
+ return program.account.config.fetch(configPda);
1082
+ }
1083
+ }
1084
+ supportedChains[ChainFamily.Solana] = SolanaChain;
1085
+ //# sourceMappingURL=index.js.map