@chainlink/ccip-sdk 0.91.1 → 0.92.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 (273) hide show
  1. package/README.md +127 -80
  2. package/dist/aptos/hasher.d.ts.map +1 -1
  3. package/dist/aptos/hasher.js +7 -6
  4. package/dist/aptos/hasher.js.map +1 -1
  5. package/dist/aptos/index.d.ts +7 -2
  6. package/dist/aptos/index.d.ts.map +1 -1
  7. package/dist/aptos/index.js +29 -20
  8. package/dist/aptos/index.js.map +1 -1
  9. package/dist/aptos/logs.d.ts +5 -3
  10. package/dist/aptos/logs.d.ts.map +1 -1
  11. package/dist/aptos/logs.js +64 -27
  12. package/dist/aptos/logs.js.map +1 -1
  13. package/dist/aptos/token.d.ts.map +1 -1
  14. package/dist/aptos/token.js +2 -1
  15. package/dist/aptos/token.js.map +1 -1
  16. package/dist/aptos/types.js +6 -6
  17. package/dist/aptos/types.js.map +1 -1
  18. package/dist/chain.d.ts +36 -11
  19. package/dist/chain.d.ts.map +1 -1
  20. package/dist/chain.js +34 -2
  21. package/dist/chain.js.map +1 -1
  22. package/dist/commits.d.ts +2 -3
  23. package/dist/commits.d.ts.map +1 -1
  24. package/dist/commits.js +19 -8
  25. package/dist/commits.js.map +1 -1
  26. package/dist/errors/CCIPError.d.ts +48 -0
  27. package/dist/errors/CCIPError.d.ts.map +1 -0
  28. package/dist/errors/CCIPError.js +65 -0
  29. package/dist/errors/CCIPError.js.map +1 -0
  30. package/dist/errors/codes.d.ts +120 -0
  31. package/dist/errors/codes.d.ts.map +1 -0
  32. package/dist/errors/codes.js +156 -0
  33. package/dist/errors/codes.js.map +1 -0
  34. package/dist/errors/index.d.ts +26 -0
  35. package/dist/errors/index.d.ts.map +1 -0
  36. package/dist/errors/index.js +51 -0
  37. package/dist/errors/index.js.map +1 -0
  38. package/dist/errors/recovery.d.ts +6 -0
  39. package/dist/errors/recovery.d.ts.map +1 -0
  40. package/dist/errors/recovery.js +118 -0
  41. package/dist/errors/recovery.js.map +1 -0
  42. package/dist/errors/specialized.d.ts +637 -0
  43. package/dist/errors/specialized.d.ts.map +1 -0
  44. package/dist/errors/specialized.js +1298 -0
  45. package/dist/errors/specialized.js.map +1 -0
  46. package/dist/errors/utils.d.ts +11 -0
  47. package/dist/errors/utils.d.ts.map +1 -0
  48. package/dist/errors/utils.js +61 -0
  49. package/dist/errors/utils.js.map +1 -0
  50. package/dist/evm/abi/CommitStore_1_5.js +1 -1
  51. package/dist/evm/abi/LockReleaseTokenPool_1_5.js +1 -1
  52. package/dist/evm/abi/OffRamp_1_5.js +1 -1
  53. package/dist/evm/abi/OnRamp_1_5.js +1 -1
  54. package/dist/evm/abi/PriceRegistry_1_2.d.ts +443 -0
  55. package/dist/evm/abi/PriceRegistry_1_2.d.ts.map +1 -0
  56. package/dist/evm/abi/PriceRegistry_1_2.js +439 -0
  57. package/dist/evm/abi/PriceRegistry_1_2.js.map +1 -0
  58. package/dist/evm/const.d.ts +1 -0
  59. package/dist/evm/const.d.ts.map +1 -1
  60. package/dist/evm/const.js +2 -0
  61. package/dist/evm/const.js.map +1 -1
  62. package/dist/evm/hasher.d.ts.map +1 -1
  63. package/dist/evm/hasher.js +7 -6
  64. package/dist/evm/hasher.js.map +1 -1
  65. package/dist/evm/index.d.ts +9 -13
  66. package/dist/evm/index.d.ts.map +1 -1
  67. package/dist/evm/index.js +85 -68
  68. package/dist/evm/index.js.map +1 -1
  69. package/dist/evm/logs.d.ts.map +1 -1
  70. package/dist/evm/logs.js +47 -16
  71. package/dist/evm/logs.js.map +1 -1
  72. package/dist/evm/messages.d.ts +7 -6
  73. package/dist/evm/messages.d.ts.map +1 -1
  74. package/dist/evm/offchain.js +1 -1
  75. package/dist/evm/offchain.js.map +1 -1
  76. package/dist/evm/types.d.ts +10 -0
  77. package/dist/evm/types.d.ts.map +1 -0
  78. package/dist/evm/types.js +2 -0
  79. package/dist/evm/types.js.map +1 -0
  80. package/dist/execution.d.ts.map +1 -1
  81. package/dist/execution.js +9 -5
  82. package/dist/execution.js.map +1 -1
  83. package/dist/extra-args.d.ts.map +1 -1
  84. package/dist/extra-args.js +4 -3
  85. package/dist/extra-args.js.map +1 -1
  86. package/dist/gas.d.ts.map +1 -1
  87. package/dist/gas.js +3 -2
  88. package/dist/gas.js.map +1 -1
  89. package/dist/hasher/hasher.d.ts.map +1 -1
  90. package/dist/hasher/hasher.js +2 -1
  91. package/dist/hasher/hasher.js.map +1 -1
  92. package/dist/hasher/merklemulti.d.ts.map +1 -1
  93. package/dist/hasher/merklemulti.js +9 -8
  94. package/dist/hasher/merklemulti.js.map +1 -1
  95. package/dist/index.d.ts +5 -2
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +6 -2
  98. package/dist/index.js.map +1 -1
  99. package/dist/offchain.d.ts.map +1 -1
  100. package/dist/offchain.js +5 -8
  101. package/dist/offchain.js.map +1 -1
  102. package/dist/requests.d.ts +1 -1
  103. package/dist/requests.d.ts.map +1 -1
  104. package/dist/requests.js +37 -43
  105. package/dist/requests.js.map +1 -1
  106. package/dist/selectors.d.ts.map +1 -1
  107. package/dist/selectors.js +22 -0
  108. package/dist/selectors.js.map +1 -1
  109. package/dist/solana/cleanup.d.ts +2 -2
  110. package/dist/solana/cleanup.d.ts.map +1 -1
  111. package/dist/solana/cleanup.js +2 -3
  112. package/dist/solana/cleanup.js.map +1 -1
  113. package/dist/solana/exec.d.ts.map +1 -1
  114. package/dist/solana/exec.js +12 -12
  115. package/dist/solana/exec.js.map +1 -1
  116. package/dist/solana/hasher.d.ts.map +1 -1
  117. package/dist/solana/hasher.js +6 -5
  118. package/dist/solana/hasher.js.map +1 -1
  119. package/dist/solana/index.d.ts +30 -13
  120. package/dist/solana/index.d.ts.map +1 -1
  121. package/dist/solana/index.js +96 -143
  122. package/dist/solana/index.js.map +1 -1
  123. package/dist/solana/logs.d.ts +15 -0
  124. package/dist/solana/logs.d.ts.map +1 -0
  125. package/dist/solana/logs.js +106 -0
  126. package/dist/solana/logs.js.map +1 -0
  127. package/dist/solana/offchain.d.ts.map +1 -1
  128. package/dist/solana/offchain.js +6 -5
  129. package/dist/solana/offchain.js.map +1 -1
  130. package/dist/solana/patchBorsh.d.ts.map +1 -1
  131. package/dist/solana/patchBorsh.js +3 -2
  132. package/dist/solana/patchBorsh.js.map +1 -1
  133. package/dist/solana/send.d.ts.map +1 -1
  134. package/dist/solana/send.js +8 -7
  135. package/dist/solana/send.js.map +1 -1
  136. package/dist/solana/utils.d.ts +7 -8
  137. package/dist/solana/utils.d.ts.map +1 -1
  138. package/dist/solana/utils.js +23 -11
  139. package/dist/solana/utils.js.map +1 -1
  140. package/dist/sui/discovery.d.ts +18 -0
  141. package/dist/sui/discovery.d.ts.map +1 -0
  142. package/dist/sui/discovery.js +116 -0
  143. package/dist/sui/discovery.js.map +1 -0
  144. package/dist/sui/events.d.ts +36 -0
  145. package/dist/sui/events.d.ts.map +1 -0
  146. package/dist/sui/events.js +179 -0
  147. package/dist/sui/events.js.map +1 -0
  148. package/dist/sui/hasher.d.ts.map +1 -1
  149. package/dist/sui/hasher.js +6 -5
  150. package/dist/sui/hasher.js.map +1 -1
  151. package/dist/sui/index.d.ts +69 -41
  152. package/dist/sui/index.d.ts.map +1 -1
  153. package/dist/sui/index.js +402 -65
  154. package/dist/sui/index.js.map +1 -1
  155. package/dist/sui/manuallyExec/encoder.d.ts +8 -0
  156. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -0
  157. package/dist/sui/manuallyExec/encoder.js +76 -0
  158. package/dist/sui/manuallyExec/encoder.js.map +1 -0
  159. package/dist/sui/manuallyExec/index.d.ts +37 -0
  160. package/dist/sui/manuallyExec/index.d.ts.map +1 -0
  161. package/dist/sui/manuallyExec/index.js +81 -0
  162. package/dist/sui/manuallyExec/index.js.map +1 -0
  163. package/dist/sui/objects.d.ts +46 -0
  164. package/dist/sui/objects.d.ts.map +1 -0
  165. package/dist/sui/objects.js +259 -0
  166. package/dist/sui/objects.js.map +1 -0
  167. package/dist/ton/bindings/offramp.d.ts +48 -0
  168. package/dist/ton/bindings/offramp.d.ts.map +1 -0
  169. package/dist/ton/bindings/offramp.js +63 -0
  170. package/dist/ton/bindings/offramp.js.map +1 -0
  171. package/dist/ton/bindings/onramp.d.ts +40 -0
  172. package/dist/ton/bindings/onramp.d.ts.map +1 -0
  173. package/dist/ton/bindings/onramp.js +51 -0
  174. package/dist/ton/bindings/onramp.js.map +1 -0
  175. package/dist/ton/bindings/router.d.ts +47 -0
  176. package/dist/ton/bindings/router.d.ts.map +1 -0
  177. package/dist/ton/bindings/router.js +51 -0
  178. package/dist/ton/bindings/router.js.map +1 -0
  179. package/dist/ton/exec.d.ts +18 -0
  180. package/dist/ton/exec.d.ts.map +1 -0
  181. package/dist/ton/exec.js +28 -0
  182. package/dist/ton/exec.js.map +1 -0
  183. package/dist/ton/hasher.d.ts +27 -0
  184. package/dist/ton/hasher.d.ts.map +1 -0
  185. package/dist/ton/hasher.js +134 -0
  186. package/dist/ton/hasher.js.map +1 -0
  187. package/dist/ton/index.d.ts +247 -0
  188. package/dist/ton/index.d.ts.map +1 -0
  189. package/dist/ton/index.js +781 -0
  190. package/dist/ton/index.js.map +1 -0
  191. package/dist/ton/logs.d.ts +26 -0
  192. package/dist/ton/logs.d.ts.map +1 -0
  193. package/dist/ton/logs.js +126 -0
  194. package/dist/ton/logs.js.map +1 -0
  195. package/dist/ton/types.d.ts +37 -0
  196. package/dist/ton/types.d.ts.map +1 -0
  197. package/dist/ton/types.js +92 -0
  198. package/dist/ton/types.js.map +1 -0
  199. package/dist/ton/utils.d.ts +67 -0
  200. package/dist/ton/utils.d.ts.map +1 -0
  201. package/dist/ton/utils.js +425 -0
  202. package/dist/ton/utils.js.map +1 -0
  203. package/dist/types.d.ts +4 -2
  204. package/dist/types.d.ts.map +1 -1
  205. package/dist/types.js +1 -0
  206. package/dist/types.js.map +1 -1
  207. package/dist/utils.d.ts +10 -0
  208. package/dist/utils.d.ts.map +1 -1
  209. package/dist/utils.js +52 -17
  210. package/dist/utils.js.map +1 -1
  211. package/package.json +12 -10
  212. package/src/aptos/hasher.ts +10 -6
  213. package/src/aptos/index.ts +50 -31
  214. package/src/aptos/logs.ts +85 -29
  215. package/src/aptos/token.ts +5 -1
  216. package/src/aptos/types.ts +6 -6
  217. package/src/chain.ts +83 -12
  218. package/src/commits.ts +23 -11
  219. package/src/errors/CCIPError.ts +86 -0
  220. package/src/errors/codes.ts +179 -0
  221. package/src/errors/index.ts +175 -0
  222. package/src/errors/recovery.ts +170 -0
  223. package/src/errors/specialized.ts +1655 -0
  224. package/src/errors/utils.ts +73 -0
  225. package/src/evm/abi/CommitStore_1_5.ts +1 -1
  226. package/src/evm/abi/LockReleaseTokenPool_1_5.ts +1 -1
  227. package/src/evm/abi/OffRamp_1_5.ts +1 -1
  228. package/src/evm/abi/OnRamp_1_5.ts +1 -1
  229. package/src/evm/abi/PriceRegistry_1_2.ts +438 -0
  230. package/src/evm/const.ts +2 -0
  231. package/src/evm/hasher.ts +7 -6
  232. package/src/evm/index.ts +104 -86
  233. package/src/evm/logs.ts +64 -16
  234. package/src/evm/messages.ts +14 -14
  235. package/src/evm/offchain.ts +1 -1
  236. package/src/evm/types.ts +11 -0
  237. package/src/execution.ts +13 -9
  238. package/src/extra-args.ts +4 -3
  239. package/src/gas.ts +10 -3
  240. package/src/hasher/hasher.ts +2 -1
  241. package/src/hasher/merklemulti.ts +18 -8
  242. package/src/index.ts +14 -2
  243. package/src/offchain.ts +10 -14
  244. package/src/requests.ts +51 -53
  245. package/src/selectors.ts +23 -0
  246. package/src/solana/cleanup.ts +2 -4
  247. package/src/solana/exec.ts +13 -13
  248. package/src/solana/hasher.ts +9 -5
  249. package/src/solana/index.ts +126 -200
  250. package/src/solana/logs.ts +155 -0
  251. package/src/solana/offchain.ts +10 -7
  252. package/src/solana/patchBorsh.ts +3 -2
  253. package/src/solana/send.ts +14 -7
  254. package/src/solana/utils.ts +31 -17
  255. package/src/sui/discovery.ts +163 -0
  256. package/src/sui/events.ts +328 -0
  257. package/src/sui/hasher.ts +6 -5
  258. package/src/sui/index.ts +528 -80
  259. package/src/sui/manuallyExec/encoder.ts +88 -0
  260. package/src/sui/manuallyExec/index.ts +137 -0
  261. package/src/sui/objects.ts +358 -0
  262. package/src/ton/bindings/offramp.ts +96 -0
  263. package/src/ton/bindings/onramp.ts +72 -0
  264. package/src/ton/bindings/router.ts +65 -0
  265. package/src/ton/exec.ts +44 -0
  266. package/src/ton/hasher.ts +184 -0
  267. package/src/ton/index.ts +989 -0
  268. package/src/ton/logs.ts +157 -0
  269. package/src/ton/types.ts +143 -0
  270. package/src/ton/utils.ts +514 -0
  271. package/src/types.ts +6 -2
  272. package/src/utils.ts +58 -23
  273. package/tsconfig.json +2 -1
@@ -0,0 +1,155 @@
1
+ import { type Connection, PublicKey } from '@solana/web3.js'
2
+
3
+ import type { LogFilter } from '../chain.ts'
4
+ import type { SolanaTransaction } from './index.ts'
5
+ import {
6
+ CCIPLogsAddressRequiredError,
7
+ CCIPLogsWatchRequiresFinalityError,
8
+ CCIPLogsWatchRequiresStartError,
9
+ } from '../errors/index.ts'
10
+ import { sleep } from '../utils.ts'
11
+
12
+ const DEFAULT_POLL_INTERVAL = 5e3
13
+
14
+ async function* fetchSigsForward(
15
+ opts: LogFilter & { pollInterval?: number },
16
+ ctx: { connection: Connection },
17
+ ) {
18
+ const { connection } = ctx
19
+ const limit = Math.min(opts?.page || 1000, 1000)
20
+ const commitment = opts.endBlock === 'finalized' ? 'finalized' : 'confirmed'
21
+
22
+ // forward collect all matching sigs in array
23
+ const allSigs = [] as Awaited<ReturnType<typeof connection.getSignaturesForAddress>>
24
+ let batch: typeof allSigs, until: string
25
+ do {
26
+ batch = await connection.getSignaturesForAddress(
27
+ new PublicKey(opts.address!),
28
+ { limit, before: allSigs[allSigs.length - 1]?.signature ?? opts.endBefore },
29
+ commitment,
30
+ )
31
+ until ??= batch[0]?.signature
32
+
33
+ while (
34
+ batch.length > 0 &&
35
+ (batch[batch.length - 1].slot < (opts.startBlock || 0) ||
36
+ (batch[batch.length - 1].blockTime || -1) < (opts.startTime || 0))
37
+ ) {
38
+ batch.length-- // truncate tail of txs which are older than requested start
39
+ }
40
+
41
+ allSigs.push(...batch) // concat in descending order
42
+ } while (batch.length >= limit)
43
+
44
+ allSigs.reverse() // forward
45
+
46
+ const notAfter =
47
+ typeof opts.endBlock !== 'number'
48
+ ? undefined
49
+ : opts.endBlock < 0
50
+ ? (await connection.getSlot('confirmed')) + opts.endBlock
51
+ : opts.endBlock
52
+ while (notAfter && allSigs.length > 0 && allSigs[allSigs.length - 1].slot > notAfter) {
53
+ allSigs.length-- // truncate head (after reverse) of txs newer than requested end
54
+ }
55
+ yield* allSigs // all past logs
56
+
57
+ if (allSigs.length) until = allSigs[allSigs.length - 1].signature
58
+ let lastReq = performance.now()
59
+ // if not watch mode, returns
60
+ while (opts.watch) {
61
+ let break$ = sleep(
62
+ Math.max((opts.pollInterval || DEFAULT_POLL_INTERVAL) - (performance.now() - lastReq), 1),
63
+ ).then(() => false)
64
+ if (opts.watch instanceof Promise) break$ = Promise.race([break$, opts.watch.then(() => true)])
65
+ if (await break$) break
66
+
67
+ lastReq = performance.now()
68
+ batch = await connection.getSignaturesForAddress(
69
+ new PublicKey(opts.address!),
70
+ { limit, until },
71
+ commitment,
72
+ )
73
+
74
+ batch.reverse() // forward
75
+
76
+ const notAfter =
77
+ batch.length === 0 || typeof opts.endBlock !== 'number'
78
+ ? undefined
79
+ : opts.endBlock < 0
80
+ ? (await connection.getSlot('confirmed')) + opts.endBlock
81
+ : opts.endBlock
82
+
83
+ for (const sig of batch) {
84
+ if (notAfter && sig.slot > notAfter) break
85
+ until = sig.signature
86
+ yield sig
87
+ }
88
+ }
89
+ }
90
+
91
+ async function* fetchSigsBackwards(
92
+ opts: LogFilter & { pollInterval?: number },
93
+ ctx: { connection: Connection },
94
+ ) {
95
+ const { connection } = ctx
96
+ const limit = Math.min(opts?.page || 1000, 1000)
97
+ const commitment = opts.endBlock === 'finalized' ? 'finalized' : 'confirmed'
98
+
99
+ if (typeof opts.endBlock === 'number' && opts.endBlock < 0)
100
+ opts.endBlock = (await connection.getSlot('confirmed')) + opts.endBlock
101
+
102
+ let batch: Awaited<ReturnType<typeof connection.getSignaturesForAddress>> | undefined
103
+ do {
104
+ batch = await connection.getSignaturesForAddress(
105
+ new PublicKey(opts.address!),
106
+ {
107
+ limit,
108
+ before: batch?.length
109
+ ? batch[batch.length - 1].signature
110
+ : opts.endBefore
111
+ ? opts.endBefore
112
+ : undefined,
113
+ },
114
+ commitment,
115
+ )
116
+ for (const sig of batch) {
117
+ if (typeof opts.endBlock === 'number' && sig.slot > opts.endBlock) continue
118
+ yield sig
119
+ }
120
+ } while (batch.length >= limit)
121
+ }
122
+
123
+ /**
124
+ * Internal method to get transactions for an address with pagination.
125
+ * @param opts - Log filter options.
126
+ * @returns Async generator of Solana transactions.
127
+ */
128
+ export async function* getTransactionsForAddress(
129
+ opts: Omit<LogFilter, 'topics'> & { pollInterval?: number },
130
+ ctx: {
131
+ connection: Connection
132
+ getTransaction: (signature: string) => Promise<SolanaTransaction>
133
+ },
134
+ ): AsyncGenerator<SolanaTransaction> {
135
+ if (!opts.address) throw new CCIPLogsAddressRequiredError()
136
+
137
+ opts.endBlock ||= 'latest'
138
+
139
+ let allSignatures
140
+ if (opts.startBlock != null || opts.startTime != null) {
141
+ if (opts.watch && ((typeof opts.endBlock === 'number' && opts.endBlock > 0) || opts.endBefore))
142
+ throw new CCIPLogsWatchRequiresFinalityError(opts.endBlock)
143
+
144
+ allSignatures = fetchSigsForward(opts, ctx)
145
+ } else {
146
+ if (opts.watch) throw new CCIPLogsWatchRequiresStartError()
147
+
148
+ allSignatures = fetchSigsBackwards(opts, ctx) // generate backwards until depleting getSignaturesForAddress
149
+ }
150
+
151
+ // Process signatures
152
+ for await (const signatureInfo of allSignatures) {
153
+ yield await ctx.getTransaction(signatureInfo.signature)
154
+ }
155
+ }
@@ -2,13 +2,18 @@ import { type BN, BorshCoder } from '@coral-xyz/anchor'
2
2
  import type { PublicKey } from '@solana/web3.js'
3
3
  import { hexlify } from 'ethers'
4
4
 
5
+ import {
6
+ CCIPCctpDecodeError,
7
+ CCIPCctpMultipleEventsError,
8
+ CCIPDataFormatUnsupportedError,
9
+ } from '../errors/index.ts'
5
10
  import { getUsdcAttestation } from '../offchain.ts'
6
11
  import type { CCIPMessage, CCIPRequest, OffchainTokenData, WithLogger } from '../types.ts'
7
- import { networkInfo, util } from '../utils.ts'
12
+ import { bytesToBuffer, networkInfo, util } from '../utils.ts'
8
13
  import { IDL as BASE_TOKEN_POOL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
9
14
  import { IDL as CCTP_TOKEN_POOL } from './idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts'
10
15
  import type { SolanaLog, SolanaTransaction } from './index.ts'
11
- import { bytesToBuffer, hexDiscriminator } from './utils.ts'
16
+ import { hexDiscriminator } from './utils.ts'
12
17
 
13
18
  interface CcipCctpMessageSentEvent {
14
19
  originalSender: PublicKey
@@ -53,7 +58,7 @@ export async function fetchSolanaOffchainTokenData(
53
58
  }
54
59
 
55
60
  if (request.message.tokenAmounts.length > 1) {
56
- throw new Error(
61
+ throw new CCIPDataFormatUnsupportedError(
57
62
  `Expected at most 1 token transfer, found ${request.message.tokenAmounts?.length}`,
58
63
  )
59
64
  }
@@ -75,7 +80,7 @@ export async function fetchSolanaOffchainTokenData(
75
80
  if (requestInvokeIdx >= l.index || l.index >= log.index) continue
76
81
  if (l.topics[0] !== hexDiscriminator('CcipCctpMessageSentEvent')) continue
77
82
  const decoded = cctpTokenPoolCoder.events.decode(l.data)
78
- if (!decoded) throw new Error(`Failed to decode CCTP event: ${util.inspect(l)}`)
83
+ if (!decoded) throw new CCIPCctpDecodeError(util.inspect(l))
79
84
  cctpEvents.push(decoded.data as unknown as CcipCctpMessageSentEvent)
80
85
  }
81
86
  const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(() => undefined)
@@ -88,9 +93,7 @@ export async function fetchSolanaOffchainTokenData(
88
93
 
89
94
  // Currently, we only support ONE token per transfer
90
95
  if (cctpEvents.length > 1) {
91
- throw new Error(
92
- `Expected only 1 CcipCctpMessageSentEvent, found ${cctpEvents.length} in transaction ${txSignature}.`,
93
- )
96
+ throw new CCIPCctpMultipleEventsError(cctpEvents.length, txSignature)
94
97
  }
95
98
 
96
99
  // NOTE: assuming USDC token is the first (and only) token in the CCIP message, we will process the CCTP event.
@@ -4,6 +4,7 @@ import { BorshInstructionCoder } from '@coral-xyz/anchor'
4
4
  import { BorshTypesCoder } from '@coral-xyz/anchor/dist/cjs/coder/borsh/types.js'
5
5
  import { sha256, toUtf8Bytes } from 'ethers'
6
6
 
7
+ import { CCIPBorshMethodUnknownError, CCIPBorshTypeUnknownError } from '../errors/index.ts'
7
8
  import { snakeToCamel } from '../utils.ts'
8
9
  import { camelToSnakeCase } from './utils.ts'
9
10
 
@@ -30,7 +31,7 @@ export function patchBorsh() {
30
31
  name,
31
32
  )
32
33
  if (!layout) {
33
- throw new Error(`Unknown type: ${name}`)
34
+ throw new CCIPBorshTypeUnknownError(name)
34
35
  }
35
36
  let buffer = Buffer.alloc(512)
36
37
  let len
@@ -61,7 +62,7 @@ export function patchBorsh() {
61
62
  methodName,
62
63
  )
63
64
  if (!layout) {
64
- throw new Error(`Unknown method: ${methodName}`)
65
+ throw new CCIPBorshMethodUnknownError(methodName)
65
66
  }
66
67
  let buffer = Buffer.alloc(512)
67
68
  let len
@@ -18,11 +18,18 @@ import BN from 'bn.js'
18
18
  import { zeroPadValue } from 'ethers'
19
19
 
20
20
  import { SolanaChain } from './index.ts'
21
+ import {
22
+ CCIPSolanaFeeResultInvalidError,
23
+ CCIPSolanaLookupTableNotFoundError,
24
+ CCIPSolanaRouterConfigNotFoundError,
25
+ CCIPTokenAmountInvalidError,
26
+ CCIPTokenMintNotFoundError,
27
+ } from '../errors/index.ts'
21
28
  import { type AnyMessage, type WithLogger, ChainFamily } from '../types.ts'
22
- import { toLeArray, util } from '../utils.ts'
29
+ import { bytesToBuffer, toLeArray, util } from '../utils.ts'
23
30
  import { IDL as CCIP_ROUTER_IDL } from './idl/1.6.0/CCIP_ROUTER.ts'
24
31
  import type { UnsignedSolanaTx } from './types.ts'
25
- import { bytesToBuffer, simulationProvider } from './utils.ts'
32
+ import { simulationProvider } from './utils.ts'
26
33
 
27
34
  function anyToSvmMessage(message: AnyMessage): IdlTypes<typeof CCIP_ROUTER_IDL>['SVM2AnyMessage'] {
28
35
  const feeTokenPubkey = message.feeToken ? new PublicKey(message.feeToken) : PublicKey.default
@@ -32,7 +39,7 @@ function anyToSvmMessage(message: AnyMessage): IdlTypes<typeof CCIP_ROUTER_IDL>[
32
39
  data: bytesToBuffer(message.data || '0x'),
33
40
  tokenAmounts: (message.tokenAmounts || []).map((ta) => {
34
41
  if (!ta.token || ta.amount < 0n) {
35
- throw new Error('Invalid token amount: token address and positive amount required')
42
+ throw new CCIPTokenAmountInvalidError()
36
43
  }
37
44
  return {
38
45
  token: new PublicKey(ta.token),
@@ -66,7 +73,7 @@ export async function getFee(
66
73
  // Get router config to find feeQuoter
67
74
  const [configPda] = PublicKey.findProgramAddressSync([Buffer.from('config')], program.programId)
68
75
  const configAccount = await connection.getAccountInfo(configPda)
69
- if (!configAccount) throw new Error(`Router config not found at ${configPda.toBase58()}`)
76
+ if (!configAccount) throw new CCIPSolanaRouterConfigNotFoundError(configPda.toBase58())
70
77
 
71
78
  const { feeQuoter, linkTokenMint }: { feeQuoter: PublicKey; linkTokenMint: PublicKey } =
72
79
  program.coder.accounts.decode('config', configAccount.data)
@@ -147,7 +154,7 @@ export async function getFee(
147
154
  .view()) as IdlTypes<typeof CCIP_ROUTER_IDL>['GetFeeResult']
148
155
 
149
156
  if (!result?.amount) {
150
- throw new Error(`Invalid fee result from router: ${util.inspect(result)}`)
157
+ throw new CCIPSolanaFeeResultInvalidError(util.inspect(result))
151
158
  }
152
159
 
153
160
  return BigInt(result.amount.toString())
@@ -224,7 +231,7 @@ async function deriveAccountsCcipSend({
224
231
  const lookupTableAccountInfo = await connection.getAddressLookupTable(table)
225
232
 
226
233
  if (!lookupTableAccountInfo.value) {
227
- throw new Error(`Lookup table account not found: ${table.toBase58()}`)
234
+ throw new CCIPSolanaLookupTableNotFoundError(table.toBase58())
228
235
  }
229
236
 
230
237
  return lookupTableAccountInfo.value
@@ -332,7 +339,7 @@ async function approveRouterSpender(
332
339
  ): Promise<TransactionInstruction | undefined> {
333
340
  // Get the current account info to check existing delegation (or create if needed)
334
341
  const mintInfo = await connection.getAccountInfo(token)
335
- if (!mintInfo) throw new Error(`Mint ${token.toBase58()} not found`)
342
+ if (!mintInfo) throw new CCIPTokenMintNotFoundError(token.toBase58())
336
343
  const associatedTokenAccount = getAssociatedTokenAddressSync(
337
344
  token,
338
345
  owner,
@@ -1,6 +1,4 @@
1
- import { Buffer } from 'buffer'
2
-
3
- import { eventDiscriminator } from '@coral-xyz/anchor'
1
+ import { type IdlTypes, eventDiscriminator } from '@coral-xyz/anchor'
4
2
  import {
5
3
  type AddressLookupTableAccount,
6
4
  type Connection,
@@ -14,11 +12,17 @@ import {
14
12
  TransactionMessage,
15
13
  VersionedTransaction,
16
14
  } from '@solana/web3.js'
17
- import { type BytesLike, dataLength, dataSlice, hexlify } from 'ethers'
15
+ import { dataLength, dataSlice, hexlify } from 'ethers'
18
16
 
17
+ import {
18
+ CCIPSolanaComputeUnitsExceededError,
19
+ CCIPTransactionNotFinalizedError,
20
+ } from '../errors/index.ts'
19
21
  import type { Log_, WithLogger } from '../types.ts'
20
22
  import { getDataBytes, sleep } from '../utils.ts'
23
+ import type { IDL as BASE_TOKEN_POOL_IDL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
21
24
  import type { UnsignedSolanaTx, Wallet } from './types.ts'
25
+ import type { RateLimiterState } from '../chain.ts'
22
26
 
23
27
  /**
24
28
  * Generates a hex-encoded discriminator for a Solana event.
@@ -29,15 +33,6 @@ export function hexDiscriminator(eventName: string): string {
29
33
  return hexlify(eventDiscriminator(eventName))
30
34
  }
31
35
 
32
- /**
33
- * Converts bytes to a Node.js Buffer.
34
- * @param bytes - Bytes to convert.
35
- * @returns Node.js Buffer.
36
- */
37
- export function bytesToBuffer(bytes: BytesLike): Buffer {
38
- return Buffer.from(getDataBytes(bytes).buffer)
39
- }
40
-
41
36
  /**
42
37
  * Waits for a Solana transaction to reach finalized status.
43
38
  * @param connection - Solana connection instance.
@@ -61,7 +56,7 @@ export async function waitForFinalization(
61
56
  await sleep(intervalMs)
62
57
  }
63
58
 
64
- throw new Error(`Transaction ${signature} not finalized after timeout`)
59
+ throw new CCIPTransactionNotFinalizedError(signature)
65
60
  }
66
61
 
67
62
  /**
@@ -361,9 +356,7 @@ export async function simulateAndSendTxs(
361
356
  } else if (!includesMain || computeUnits == null || simulated <= computeUnits) {
362
357
  computeUnitLimit = Math.ceil(simulated * 1.1)
363
358
  } else {
364
- throw new Error(
365
- `Main simulation exceeds specified computeUnits limit. simulated=${simulated}, limit=${computeUnits}`,
366
- )
359
+ throw new CCIPSolanaComputeUnitsExceededError(simulated, computeUnits)
367
360
  }
368
361
  break
369
362
  } catch (err) {
@@ -394,3 +387,24 @@ export async function simulateAndSendTxs(
394
387
  }
395
388
  return mainHash!
396
389
  }
390
+
391
+ /**
392
+ * Convert TokenPool's rate limit to RateLimiterState object
393
+ */
394
+ export function convertRateLimiter(
395
+ input: IdlTypes<typeof BASE_TOKEN_POOL_IDL>['BaseChain']['inboundRateLimit'],
396
+ ): RateLimiterState {
397
+ if (!input.cfg.enabled) return null
398
+ const tokens = BigInt(input.tokens.toString())
399
+ const out: RateLimiterState = {
400
+ capacity: BigInt(input.cfg.capacity.toString()),
401
+ rate: BigInt(input.cfg.rate.toString()),
402
+ get tokens() {
403
+ const cur =
404
+ tokens + this.rate * BigInt(Math.floor(Date.now() / 1000) - input.lastUpdated.toNumber())
405
+ if (cur < this.capacity) return cur
406
+ else return this.capacity
407
+ },
408
+ }
409
+ return out
410
+ }
@@ -0,0 +1,163 @@
1
+ import type { SuiClient } from '@mysten/sui/client'
2
+ import { Transaction } from '@mysten/sui/transactions'
3
+ import { normalizeSuiAddress } from '@mysten/sui/utils'
4
+ import { hexlify } from 'ethers'
5
+
6
+ import { CCIPError } from '../errors/CCIPError.ts'
7
+ import { CCIPErrorCode } from '../errors/codes.ts'
8
+ import { bytesToBuffer } from '../utils.ts'
9
+
10
+ /**
11
+ * Discovers the CCIP package ID associated with a given Sui onramp package.
12
+ *
13
+ * @param client - sui client
14
+ * @param onramp - sui onramp package id
15
+ * @returns ccip package id
16
+ */
17
+ export const discoverCCIP = async (client: SuiClient, onramp: string): Promise<string> => {
18
+ const tx = new Transaction()
19
+ tx.moveCall({
20
+ target: `${onramp}::onramp::get_ccip_package_id`,
21
+ })
22
+
23
+ const inspectResult = await client.devInspectTransactionBlock({
24
+ sender: normalizeSuiAddress('0x0'),
25
+ transactionBlock: tx,
26
+ })
27
+ const returnValues = inspectResult.results?.[0]?.returnValues
28
+ if (!returnValues || returnValues.length === 0) {
29
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'No return values from dev inspect')
30
+ }
31
+ const [valueBytes] = returnValues[0]
32
+ if (!valueBytes) {
33
+ throw new CCIPError(
34
+ CCIPErrorCode.UNKNOWN,
35
+ 'Unable to decode CCIP package id from return values',
36
+ )
37
+ }
38
+
39
+ return normalizeSuiAddress(hexlify(bytesToBuffer(valueBytes)))
40
+ }
41
+
42
+ /**
43
+ * Gets the Sui offramp package ID associated with a given CCIP package ID.
44
+ *
45
+ * @param client - Sui client
46
+ * @param ccip - Sui CCIP Package Id
47
+ * @returns Sui offramp package id
48
+ */
49
+ export const discoverOfframp = async (client: SuiClient, ccip: string) => {
50
+ // Get CCIP publish tx info
51
+ // Get the owner cap created in that tx.
52
+ // Get owner of the ownercap object.
53
+ // Get objects owned by that owner.
54
+ // Trough each of the objects owned by that owner, get the original transaction that created them.
55
+ // Take any of the objects created by that transaction, check its info to find the OffRamp package.
56
+ const ccipObject = await client.getObject({
57
+ id: ccip,
58
+ options: {
59
+ showPreviousTransaction: true,
60
+ },
61
+ })
62
+
63
+ // Get the tx that created the ownercap object.
64
+ const ccipCreationTxDigest = ccipObject.data?.previousTransaction
65
+ if (!ccipCreationTxDigest) {
66
+ throw new CCIPError(
67
+ CCIPErrorCode.UNKNOWN,
68
+ 'Could not find previous transaction for CCIP object',
69
+ )
70
+ }
71
+
72
+ const ccipCreationTx = await client.getTransactionBlock({
73
+ digest: ccipCreationTxDigest,
74
+ options: {
75
+ showEffects: true,
76
+ },
77
+ })
78
+
79
+ const ccipCreatedObjects = ccipCreationTx.effects?.created?.map((obj) => obj.reference.objectId)
80
+ if (!ccipCreatedObjects || ccipCreatedObjects.length === 0) {
81
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'No created objects found in creation transaction')
82
+ }
83
+
84
+ const ccipObjectsData = await Promise.all(
85
+ ccipCreatedObjects.map((objId) =>
86
+ client.getObject({
87
+ id: objId,
88
+ options: {
89
+ showType: true,
90
+ showContent: true,
91
+ showOwner: true,
92
+ },
93
+ }),
94
+ ),
95
+ )
96
+
97
+ const ownerCapObject = ccipObjectsData.find((objData) =>
98
+ objData.data?.type?.includes('::ownable::OwnerCap'),
99
+ )
100
+
101
+ if (!ownerCapObject) {
102
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'OwnerCap object not found among created objects')
103
+ }
104
+
105
+ const ownerCapOwner = ownerCapObject.data?.owner
106
+ if (!ownerCapOwner) {
107
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Could not find owner of the OwnerCap object')
108
+ }
109
+
110
+ if (typeof ownerCapOwner === 'string' || !('AddressOwner' in ownerCapOwner)) {
111
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'OwnerCap object does not have an AddressOwner')
112
+ }
113
+
114
+ const ownerCapOwnerObjects = await client.getOwnedObjects({
115
+ owner: ownerCapOwner['AddressOwner'],
116
+ })
117
+
118
+ const fullObjectsInfo = await Promise.all(
119
+ ownerCapOwnerObjects.data.map((obj) =>
120
+ client.getObject({
121
+ id: obj.data?.objectId || '',
122
+ options: {
123
+ showType: true,
124
+ },
125
+ }),
126
+ ),
127
+ )
128
+
129
+ const ownerCapPackageIds = fullObjectsInfo
130
+ .filter((objData) => objData.data?.type?.includes('::ownable::OwnerCap'))
131
+ .map((obj) => obj.data?.type?.split('::')[0])
132
+
133
+ const packagesInfo = await Promise.all(
134
+ ownerCapPackageIds.map((pkgId) =>
135
+ client.getNormalizedMoveModulesByPackage({
136
+ package: pkgId || '',
137
+ }),
138
+ ),
139
+ )
140
+
141
+ const offrampPkgs = packagesInfo
142
+ .filter((pkg) => {
143
+ return Object.values(pkg).some((module) => module.name === 'offramp')
144
+ })
145
+ .flatMap((pkg) => Object.values(pkg))
146
+ .filter((module) => module.name === 'offramp')
147
+
148
+ if (offrampPkgs.length === 0) {
149
+ throw new CCIPError(
150
+ CCIPErrorCode.UNKNOWN,
151
+ 'Could not find OffRamp package among OwnerCap packages',
152
+ )
153
+ }
154
+
155
+ if (offrampPkgs.length > 1) {
156
+ throw new CCIPError(
157
+ CCIPErrorCode.UNKNOWN,
158
+ 'Multiple OffRamp packages found; unable to uniquely identify OffRamp package',
159
+ )
160
+ }
161
+
162
+ return normalizeSuiAddress(offrampPkgs[0].address)
163
+ }