@chainlink/ccip-sdk 0.91.1 → 0.92.1

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 +176 -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 +11 -9
  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 +15 -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 +325 -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
package/src/sui/index.ts CHANGED
@@ -1,19 +1,38 @@
1
- import { type BytesLike, isBytesLike } from 'ethers'
1
+ import { toHex } from '@mysten/bcs'
2
+ import { type SuiTransactionBlockResponse, SuiClient } from '@mysten/sui/client'
3
+ import type { Keypair } from '@mysten/sui/cryptography'
4
+ import { SuiGraphQLClient } from '@mysten/sui/graphql'
5
+ import { Transaction } from '@mysten/sui/transactions'
6
+ import { type BytesLike, AbiCoder, hexlify, isBytesLike } from 'ethers'
2
7
  import type { PickDeep } from 'type-fest'
3
8
 
4
9
  import { AptosChain } from '../aptos/index.ts'
5
10
  import { type LogFilter, Chain } from '../chain.ts'
6
- import type { EVMExtraArgsV2, ExtraArgs, SVMExtraArgsV1 } from '../extra-args.ts'
11
+ import {
12
+ CCIPContractNotRouterError,
13
+ CCIPDataFormatUnsupportedError,
14
+ CCIPError,
15
+ CCIPErrorCode,
16
+ CCIPExecTxRevertedError,
17
+ CCIPExtraArgsInvalidError,
18
+ CCIPNotImplementedError,
19
+ CCIPSuiMessageVersionInvalidError,
20
+ CCIPVersionFeatureUnavailableError,
21
+ } from '../errors/index.ts'
22
+ import type { ExtraArgs, SuiExtraArgsV1 } from '../extra-args.ts'
7
23
  import { getSuiLeafHasher } from './hasher.ts'
8
24
  import type { LeafHasher } from '../hasher/common.ts'
9
25
  import { supportedChains } from '../supported-chains.ts'
10
26
  import {
11
27
  type AnyMessage,
28
+ type CCIPMessage,
12
29
  type CCIPRequest,
30
+ type CCIPVersion,
13
31
  type ChainTransaction,
14
32
  type CommitReport,
15
33
  type ExecutionReceipt,
16
34
  type ExecutionReport,
35
+ type ExecutionState,
17
36
  type Lane,
18
37
  type Log_,
19
38
  type NetworkInfo,
@@ -22,6 +41,30 @@ import {
22
41
  ChainFamily,
23
42
  } from '../types.ts'
24
43
  import type { CCIPMessage_V1_6_Sui } from './types.ts'
44
+ import { bytesToBuffer, decodeAddress, getDataBytes, networkInfo } from '../utils.ts'
45
+ import { type CommitEvent, getSuiEventsInTimeRange } from './events.ts'
46
+ import {
47
+ type SuiManuallyExecuteInput,
48
+ type TokenConfig,
49
+ buildManualExecutionPTB,
50
+ } from './manuallyExec/index.ts'
51
+ import {
52
+ fetchTokenConfigs,
53
+ getCcipObjectRef,
54
+ getOffRampStateObject,
55
+ getReceiverModule,
56
+ } from './objects.ts'
57
+ import selectors from '../selectors.ts'
58
+ import { discoverCCIP, discoverOfframp } from './discovery.ts'
59
+
60
+ export const SUI_EXTRA_ARGS_V1_TAG = '21ea4ca9' as const
61
+
62
+ type SuiContractDir = {
63
+ ccip?: string
64
+ onRamp?: string
65
+ offRamp?: string
66
+ router?: string
67
+ }
25
68
 
26
69
  /**
27
70
  * Sui chain implementation supporting Sui networks.
@@ -32,119 +75,353 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
32
75
  supportedChains[ChainFamily.Sui] = SuiChain
33
76
  }
34
77
  static readonly family = ChainFamily.Sui
35
- static readonly decimals = 8
78
+ static readonly decimals = 9 // SUI has 9 decimals
79
+
80
+ override readonly network: NetworkInfo<typeof ChainFamily.Sui>
81
+ readonly client: SuiClient
82
+ readonly graphqlClient: SuiGraphQLClient
83
+
84
+ // contracts dir <chainSelectorName, SuiContractDir>
85
+ readonly contractsDir: SuiContractDir
36
86
 
37
87
  /**
38
88
  * Creates a new SuiChain instance.
39
- * @param network - Sui network configuration.
89
+ * @param client - Sui client for interacting with the Sui network.
90
+ * @param network - Network information for this chain.
40
91
  */
41
- constructor(network: NetworkInfo<typeof ChainFamily.Sui>, ctx?: WithLogger) {
92
+ constructor(client: SuiClient, network: NetworkInfo<typeof ChainFamily.Sui>, ctx?: WithLogger) {
42
93
  super(network, ctx)
94
+
95
+ this.client = client
96
+ this.network = network
97
+ this.contractsDir = {}
98
+
99
+ // TODO: Graphql client should come from config
100
+ let graphqlUrl: string
101
+ const selector = network.chainSelector
102
+ if (selector === selectors['sui:1'].selector) {
103
+ // Sui mainnet (sui:1)
104
+ graphqlUrl = 'https://graphql.mainnet.sui.io/graphql'
105
+ } else if (selector === selectors['sui:2'].selector) {
106
+ // Sui testnet (sui:2)
107
+ graphqlUrl = 'https://graphql.testnet.sui.io/graphql'
108
+ } else {
109
+ // Localnet (sui:4) or unknown
110
+ graphqlUrl = 'https://graphql.devnet.sui.io/graphql'
111
+ }
112
+
113
+ this.graphqlClient = new SuiGraphQLClient({
114
+ url: graphqlUrl,
115
+ })
43
116
  }
44
117
 
45
118
  /**
46
119
  * Creates a SuiChain instance from an RPC URL.
47
- * @param _url - RPC endpoint URL.
120
+ * @param url - HTTP or WebSocket endpoint URL for the Sui network.
48
121
  * @returns A new SuiChain instance.
49
122
  */
50
- static async fromUrl(_url: string, _ctx?: WithLogger): Promise<SuiChain> {
51
- return Promise.reject(new Error('Not implemented'))
123
+ static async fromUrl(url: string, ctx?: WithLogger): Promise<SuiChain> {
124
+ const client = new SuiClient({ url })
125
+
126
+ // Get chain identifier from the client and map to network info format
127
+ const rawChainId = await client.getChainIdentifier().catch(() => null)
128
+ if (rawChainId === null) {
129
+ throw new CCIPDataFormatUnsupportedError(`Unable to fetch chain identifier from URL: ${url}`)
130
+ }
131
+
132
+ // Map Sui chain identifiers to our network info format
133
+ // Reference: https://docs.sui.io/guides/developer/getting-started/connect
134
+ let chainId: string
135
+ if (rawChainId === '35834a8a') {
136
+ chainId = 'sui:1' // mainnet
137
+ } else if (rawChainId === '4c78adac') {
138
+ chainId = 'sui:2' // testnet
139
+ } else if (rawChainId === 'b0c08dea') {
140
+ chainId = 'sui:4' // devnet
141
+ } else {
142
+ throw new CCIPError(
143
+ CCIPErrorCode.NETWORK_FAMILY_UNSUPPORTED,
144
+ `Unsupported Sui chain identifier: ${rawChainId}`,
145
+ )
146
+ }
147
+
148
+ const network = networkInfo(chainId) as NetworkInfo<typeof ChainFamily.Sui>
149
+ return new SuiChain(client, network, ctx)
52
150
  }
53
151
 
54
152
  /** {@inheritDoc Chain.getBlockTimestamp} */
55
- async getBlockTimestamp(_version: number | 'finalized'): Promise<number> {
56
- return Promise.reject(new Error('Not implemented'))
153
+ async getBlockTimestamp(block: number): Promise<number> {
154
+ const checkpoint = await this.client.getCheckpoint({
155
+ id: String(block),
156
+ })
157
+ return Number(checkpoint.timestampMs) / 1000
57
158
  }
58
159
 
59
160
  /** {@inheritDoc Chain.getTransaction} */
60
- async getTransaction(_hash: string | number): Promise<ChainTransaction> {
61
- return Promise.reject(new Error('Not implemented'))
161
+ async getTransaction(hash: string | number): Promise<ChainTransaction> {
162
+ // For Sui, hash should be a transaction digest (string)
163
+ const digest = typeof hash === 'number' ? String(hash) : hash
164
+
165
+ const txResponse = await this.client.getTransactionBlock({
166
+ digest,
167
+ options: {
168
+ showEvents: true,
169
+ showEffects: true,
170
+ showInput: true,
171
+ },
172
+ })
173
+
174
+ // Extract events from the transaction
175
+ const events: Log_[] = []
176
+ if (txResponse.events) {
177
+ for (let i = 0; i < txResponse.events.length; i++) {
178
+ const event = txResponse.events[i]
179
+ const eventType = event.type
180
+ const packageId = eventType.split('::')[0]
181
+ const moduleName = eventType.split('::')[1]
182
+ const eventName = eventType.split('::')[2]
183
+
184
+ events.push({
185
+ address: `${packageId}::${moduleName}`,
186
+ transactionHash: digest,
187
+ index: i,
188
+ blockNumber: Number(txResponse.checkpoint || 0),
189
+ data: event.parsedJson as Record<string, unknown>,
190
+ topics: [eventName],
191
+ })
192
+ }
193
+ }
194
+
195
+ return {
196
+ hash: digest,
197
+ logs: events,
198
+ blockNumber: Number(txResponse.checkpoint || 0),
199
+ timestamp: Number(txResponse.timestampMs || 0) / 1000,
200
+ from: txResponse.transaction?.data?.sender || '',
201
+ }
62
202
  }
63
203
 
64
204
  /** {@inheritDoc Chain.getLogs} */
65
- // eslint-disable-next-line require-yield
66
- async *getLogs(_opts: LogFilter & { versionAsHash?: boolean }) {
67
- await Promise.resolve()
68
- throw new Error('Not implemented')
205
+ async *getLogs(opts: LogFilter & { versionAsHash?: boolean }) {
206
+ if (!this.contractsDir.offRamp) {
207
+ throw new CCIPContractNotRouterError('OffRamp address not set in contracts directory', 'Sui')
208
+ }
209
+ // Extract the event type from topics
210
+ const topic = Array.isArray(opts.topics?.[0]) ? opts.topics[0][0] : opts.topics?.[0] || ''
211
+ if (!topic || topic !== 'CommitReportAccepted') {
212
+ throw new CCIPVersionFeatureUnavailableError(
213
+ 'Event type',
214
+ topic || 'unknown',
215
+ 'CommitReportAccepted',
216
+ )
217
+ }
218
+
219
+ const startTime = opts.startTime ? new Date(opts.startTime * 1000) : new Date(0)
220
+ const endTime = opts.endBlock
221
+ ? new Date(opts.endBlock)
222
+ : new Date(startTime.getTime() + 1 * 24 * 60 * 60 * 1000) // default to +24h
223
+
224
+ this.logger.info(
225
+ `Fetching Sui events of type ${topic} from ${startTime.toISOString()} to ${endTime.toISOString()}`,
226
+ )
227
+ const events = await getSuiEventsInTimeRange<CommitEvent>(
228
+ this.client,
229
+ this.graphqlClient,
230
+ `${this.contractsDir.offRamp}::offramp::CommitReportAccepted`,
231
+ startTime,
232
+ endTime,
233
+ )
234
+
235
+ for (const event of events) {
236
+ const eventData = event.contents.json
237
+ yield {
238
+ address: this.contractsDir.offRamp,
239
+ transactionHash: event.transaction?.digest || '',
240
+ index: 0, // Sui events do not have an index, set to 0
241
+ blockNumber: Number(event.transaction?.effects.checkpoint.sequenceNumber || 0),
242
+ data: eventData,
243
+ topics: [topic],
244
+ }
245
+ }
69
246
  }
70
247
 
71
248
  /** {@inheritDoc Chain.fetchRequestsInTx} */
72
249
  override async fetchRequestsInTx(_tx: string | ChainTransaction): Promise<CCIPRequest[]> {
73
- return Promise.reject(new Error('Not implemented'))
250
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.fetchRequestsInTx'))
74
251
  }
75
252
 
76
253
  /** {@inheritDoc Chain.fetchAllMessagesInBatch} */
77
254
  override async fetchAllMessagesInBatch<
78
255
  R extends PickDeep<
79
256
  CCIPRequest,
80
- 'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.header.sequenceNumber'
257
+ 'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
81
258
  >,
82
259
  >(
83
260
  _request: R,
84
261
  _commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
85
262
  _opts?: { page?: number },
86
263
  ): Promise<R['message'][]> {
87
- return Promise.reject(new Error('Not implemented'))
264
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.fetchAllMessagesInBatch'))
88
265
  }
89
266
 
90
267
  /** {@inheritDoc Chain.typeAndVersion} */
91
- async typeAndVersion(
92
- _address: string,
93
- ): Promise<
94
- | [type_: string, version: string, typeAndVersion: string]
95
- | [type_: string, version: string, typeAndVersion: string, suffix: string]
96
- > {
97
- return Promise.reject(new Error('Not implemented'))
268
+ async typeAndVersion(_address: string) {
269
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.typeAndVersion'))
98
270
  }
99
271
 
100
272
  /** {@inheritDoc Chain.getRouterForOnRamp} */
101
- getRouterForOnRamp(_onRamp: string, _destChainSelector: bigint): Promise<string> {
102
- return Promise.reject(new Error('Not implemented'))
273
+ async getRouterForOnRamp(onRamp: string, _destChainSelector: bigint): Promise<string> {
274
+ this.contractsDir.onRamp = onRamp
275
+ if (onRamp !== this.contractsDir.onRamp) {
276
+ this.contractsDir.onRamp = onRamp
277
+ }
278
+ return Promise.resolve(this.contractsDir.onRamp)
103
279
  }
104
280
 
105
281
  /** {@inheritDoc Chain.getRouterForOffRamp} */
106
- getRouterForOffRamp(_offRamp: string, _sourceChainSelector: bigint): Promise<string> {
107
- return Promise.reject(new Error('Not implemented'))
282
+ getRouterForOffRamp(offRamp: string, _sourceChainSelector: bigint): Promise<string> {
283
+ throw new CCIPContractNotRouterError(offRamp, 'unknown')
108
284
  }
109
285
 
110
286
  /** {@inheritDoc Chain.getNativeTokenForRouter} */
111
287
  getNativeTokenForRouter(_router: string): Promise<string> {
112
- return Promise.reject(new Error('Not implemented'))
288
+ // SUI native token is always 0x2::sui::SUI
289
+ return Promise.resolve('0x2::sui::SUI')
113
290
  }
114
291
 
115
292
  /** {@inheritDoc Chain.getOffRampsForRouter} */
116
- getOffRampsForRouter(_router: string, _sourceChainSelector: bigint): Promise<string[]> {
117
- return Promise.reject(new Error('Not implemented'))
293
+ async getOffRampsForRouter(router: string, _sourceChainSelector: bigint): Promise<string[]> {
294
+ const ccip = await discoverCCIP(this.client, router)
295
+ const offramp = await discoverOfframp(this.client, ccip)
296
+ this.contractsDir.offRamp = offramp
297
+ this.contractsDir.ccip = ccip
298
+ return [offramp]
118
299
  }
119
300
 
120
301
  /** {@inheritDoc Chain.getOnRampForRouter} */
121
302
  getOnRampForRouter(_router: string, _destChainSelector: bigint): Promise<string> {
122
- return Promise.reject(new Error('Not implemented'))
303
+ if (!this.contractsDir.onRamp) {
304
+ throw new CCIPContractNotRouterError('OnRamp address not set in contracts directory', 'Sui')
305
+ }
306
+ return Promise.resolve(this.contractsDir.onRamp)
123
307
  }
124
308
 
125
309
  /** {@inheritDoc Chain.getOnRampForOffRamp} */
126
- async getOnRampForOffRamp(_offRamp: string, _sourceChainSelector: bigint): Promise<string> {
127
- return Promise.reject(new Error('Not implemented'))
310
+ async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
311
+ if (!this.contractsDir.ccip) {
312
+ throw new CCIPError(CCIPErrorCode.UNKNOWN, 'CCIP address not set in contracts directory')
313
+ }
314
+ const offrampPackageId = offRamp
315
+ const functionName = 'get_source_chain_config'
316
+ const target = `${offrampPackageId}::offramp::${functionName}`
317
+
318
+ // Get the OffRampState object
319
+ const offrampStateObject = await getOffRampStateObject(this.client, offrampPackageId)
320
+ const ccipObjectRef = await getCcipObjectRef(this.client, this.contractsDir.ccip)
321
+ // Use the Transaction builder to create a move call
322
+ const tx = new Transaction()
323
+
324
+ // Add move call to the transaction with OffRampState object and source chain selector
325
+ tx.moveCall({
326
+ target,
327
+ arguments: [
328
+ tx.object(ccipObjectRef),
329
+ tx.object(offrampStateObject),
330
+ tx.pure.u64(sourceChainSelector),
331
+ ],
332
+ })
333
+
334
+ // Execute with devInspectTransactionBlock for read-only call
335
+ const result = await this.client.devInspectTransactionBlock({
336
+ sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
337
+ transactionBlock: tx,
338
+ })
339
+
340
+ if (result.effects.status.status !== 'success' || !result.results?.[0]?.returnValues?.[0]) {
341
+ throw new CCIPDataFormatUnsupportedError(
342
+ `Failed to call ${target}: ${result.effects.status.error || 'No return value'}`,
343
+ )
344
+ }
345
+
346
+ // The return value is a SourceChainConfig struct with the following fields:
347
+ // - Router (address = 32 bytes)
348
+ // - IsEnabled (bool = 1 byte)
349
+ // - MinSeqNr (u64 = 8 bytes)
350
+ // - IsRmnVerificationDisabled (bool = 1 byte)
351
+ // - OnRamp (vector<u8> = length + bytes)
352
+ const returnValue = result.results[0].returnValues[0]
353
+ const [data] = returnValue
354
+ const configBytes = new Uint8Array(data)
355
+
356
+ let offset = 0
357
+
358
+ // Skip Router (32 bytes)
359
+ offset += 32
360
+
361
+ // Skip IsEnabled (1 byte)
362
+ offset += 1
363
+
364
+ // Skip MinSeqNr (8 bytes)
365
+ offset += 8
366
+
367
+ // Skip IsRmnVerificationDisabled (1 byte)
368
+ offset += 1
369
+
370
+ // OnRamp (vector<u8>)
371
+ const onRampLength = configBytes[offset]
372
+ offset += 1
373
+ const onRampBytes = configBytes.slice(offset, offset + onRampLength)
374
+
375
+ // Decode the address from the onRamp bytes
376
+ return decodeAddress(onRampBytes, networkInfo(sourceChainSelector).family)
128
377
  }
129
378
 
130
379
  /** {@inheritDoc Chain.getCommitStoreForOffRamp} */
131
- getCommitStoreForOffRamp(_offRamp: string): Promise<string> {
132
- return Promise.reject(new Error('Not implemented'))
380
+ getCommitStoreForOffRamp(offRamp: string): Promise<string> {
381
+ return Promise.resolve(offRamp)
133
382
  }
134
383
 
135
384
  /** {@inheritDoc Chain.getTokenForTokenPool} */
136
- async getTokenForTokenPool(_tokenPool: string): Promise<string> {
137
- return Promise.reject(new Error('Not implemented'))
385
+ getTokenForTokenPool(_tokenPool: string): Promise<string> {
386
+ throw new CCIPNotImplementedError()
138
387
  }
139
388
 
140
389
  /** {@inheritDoc Chain.getTokenInfo} */
141
- async getTokenInfo(_token: string): Promise<{ symbol: string; decimals: number }> {
142
- return Promise.reject(new Error('Not implemented'))
390
+ async getTokenInfo(token: string): Promise<{ symbol: string; decimals: number }> {
391
+ // Handle native SUI token
392
+ if (token === '0x2::sui::SUI' || token.includes('::sui::SUI')) {
393
+ return { symbol: 'SUI', decimals: 9 }
394
+ }
395
+
396
+ try {
397
+ // For Coin types, try to fetch metadata from the coin metadata object
398
+ // Format: 0xPACKAGE::module::TYPE
399
+ const coinMetadata = await this.client.getCoinMetadata({ coinType: token })
400
+
401
+ if (coinMetadata) {
402
+ return {
403
+ symbol: coinMetadata.symbol || 'UNKNOWN',
404
+ decimals: coinMetadata.decimals,
405
+ }
406
+ }
407
+ } catch (error) {
408
+ console.log(`Failed to fetch coin metadata for ${token}:`, error)
409
+ }
410
+
411
+ // Fallback: parse from token type string if possible
412
+ const parts = token.split('::')
413
+ const symbol = parts[parts.length - 1] || 'UNKNOWN'
414
+
415
+ return {
416
+ symbol: symbol.toUpperCase(),
417
+ decimals: 9, // Default to 9 decimals (SUI standard)
418
+ }
143
419
  }
144
420
 
421
+ /** {@inheritDoc Chain.getTokenAdminRegistryFor} */
145
422
  /** {@inheritDoc Chain.getTokenAdminRegistryFor} */
146
423
  getTokenAdminRegistryFor(_address: string): Promise<string> {
147
- return Promise.reject(new Error('Not implemented'))
424
+ return Promise.reject(new CCIPNotImplementedError())
148
425
  }
149
426
 
150
427
  // Static methods for decoding
@@ -154,7 +431,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
154
431
  * @returns Decoded CCIPMessage or undefined if not valid.
155
432
  */
156
433
  static decodeMessage(_log: Log_): CCIPMessage_V1_6_Sui | undefined {
157
- throw new Error('Not implemented')
434
+ throw new CCIPNotImplementedError()
158
435
  }
159
436
 
160
437
  /**
@@ -162,41 +439,120 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
162
439
  * @param extraArgs - Encoded extra arguments bytes.
163
440
  * @returns Decoded extra arguments or undefined if unknown format.
164
441
  */
165
- static decodeExtraArgs(
166
- extraArgs: BytesLike,
167
- ):
168
- | (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
169
- | (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
170
- | undefined {
171
- return AptosChain.decodeExtraArgs(extraArgs)
442
+ static decodeExtraArgs(extraArgs: BytesLike): SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' } {
443
+ const data = getDataBytes(extraArgs)
444
+ const hexBytes = toHex(data)
445
+ if (!hexBytes.startsWith(SUI_EXTRA_ARGS_V1_TAG)) {
446
+ throw new CCIPExtraArgsInvalidError('Sui', hexBytes)
447
+ }
448
+
449
+ const abiData = '0x' + hexBytes.slice(8)
450
+ const decoded = AbiCoder.defaultAbiCoder().decode(
451
+ ['tuple(uint256,bool,bytes32,bytes32[])'],
452
+ abiData,
453
+ )
454
+
455
+ const tuple = decoded[0] as readonly [bigint, boolean, string, string[]]
456
+
457
+ return {
458
+ gasLimit: tuple[0],
459
+ allowOutOfOrderExecution: tuple[1],
460
+ tokenReceiver: tuple[2],
461
+ receiverObjectIds: tuple[3], // Already an array of hex strings
462
+ _tag: 'SuiExtraArgsV1',
463
+ }
172
464
  }
173
465
 
174
466
  /**
175
- * Encodes extra arguments for Sui CCIP messages.
176
- * @param extraArgs - Extra arguments to encode.
177
- * @returns Encoded extra arguments as hex string.
467
+ * Encodes extra arguments for CCIP messages.
468
+ * @param _extraArgs - Extra arguments to encode.
469
+ * @returns Encoded extra arguments as a hex string.
178
470
  */
179
- static encodeExtraArgs(extraArgs: ExtraArgs): string {
180
- return AptosChain.encodeExtraArgs(extraArgs)
471
+ static encodeExtraArgs(_extraArgs: ExtraArgs): string {
472
+ throw new CCIPNotImplementedError()
181
473
  }
182
474
 
183
475
  /**
184
- * Decodes commit reports from a Sui log event.
185
- * @param _log - Log event data.
186
- * @param _lane - Lane info for filtering.
187
- * @returns Array of CommitReport or undefined if not valid.
476
+ * Decodes commit reports from a log entry.
477
+ * @param log - The log entry to decode.
478
+ * @param _lane - Optional lane information.
479
+ * @returns Array of decoded commit reports or undefined.
188
480
  */
189
- static decodeCommits(_log: Log_, _lane?: Lane): CommitReport[] | undefined {
190
- throw new Error('Not implemented')
481
+ static decodeCommits(log: Log_, _lane?: Lane): CommitReport[] | undefined {
482
+ if (!log.data || typeof log.data !== 'object' || !('unblessed_merkle_roots' in log.data)) {
483
+ return
484
+ }
485
+ const toHexFromBase64 = (b64: string) => '0x' + Buffer.from(b64, 'base64').toString('hex')
486
+
487
+ const eventData = log.data as CommitEvent
488
+ const unblessedRoots = eventData.unblessed_merkle_roots
489
+ if (!Array.isArray(unblessedRoots) || unblessedRoots.length === 0) {
490
+ return
491
+ }
492
+
493
+ return unblessedRoots.map((root) => {
494
+ return {
495
+ sourceChainSelector: BigInt(root.source_chain_selector),
496
+ onRampAddress: toHexFromBase64(root.on_ramp_address),
497
+ minSeqNr: BigInt(root.min_seq_nr),
498
+ maxSeqNr: BigInt(root.max_seq_nr),
499
+ merkleRoot: toHexFromBase64(root.merkle_root),
500
+ }
501
+ })
191
502
  }
192
503
 
193
504
  /**
194
- * Decodes an execution receipt from a Sui log event.
195
- * @param _log - Log event data.
196
- * @returns ExecutionReceipt or undefined if not valid.
505
+ * Decodes an execution receipt from a log entry.
506
+ * @param log - The log entry to decode.
507
+ * @returns Decoded execution receipt or undefined.
197
508
  */
198
- static decodeReceipt(_log: Log_): ExecutionReceipt | undefined {
199
- throw new Error('Not implemented')
509
+ static decodeReceipt(log: Log_): ExecutionReceipt | undefined {
510
+ // Check if this is an ExecutionStateChanged event
511
+ const topic = (Array.isArray(log.topics) ? log.topics[0] : log.topics) as string
512
+ if (topic !== 'ExecutionStateChanged') {
513
+ return undefined
514
+ }
515
+
516
+ // Validate log data structure
517
+ if (!log.data || typeof log.data !== 'object') {
518
+ return undefined
519
+ }
520
+
521
+ const eventData = log.data as {
522
+ message_hash?: number[]
523
+ message_id?: number[]
524
+ sequence_number?: string
525
+ source_chain_selector?: string
526
+ state?: number
527
+ }
528
+
529
+ // Verify required fields exist
530
+ if (
531
+ !eventData.message_id ||
532
+ !Array.isArray(eventData.message_id) ||
533
+ eventData.sequence_number === undefined ||
534
+ eventData.state === undefined
535
+ ) {
536
+ return undefined
537
+ }
538
+
539
+ const toHex = (bytes: BytesLike | number[]) => hexlify(bytesToBuffer(bytes))
540
+
541
+ // Convert message_id bytes array to hex string
542
+ const messageId = toHex(eventData.message_id)
543
+
544
+ // Convert message_hash bytes array to hex string (if present)
545
+ const messageHash = eventData.message_hash ? toHex(eventData.message_hash) : undefined
546
+
547
+ return {
548
+ messageId,
549
+ sequenceNumber: BigInt(eventData.sequence_number),
550
+ state: eventData.state as ExecutionState,
551
+ sourceChainSelector: eventData.source_chain_selector
552
+ ? BigInt(eventData.source_chain_selector)
553
+ : undefined,
554
+ messageHash,
555
+ }
200
556
  }
201
557
 
202
558
  /**
@@ -208,6 +564,13 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
208
564
  return AptosChain.getAddress(bytes)
209
565
  }
210
566
 
567
+ /**
568
+ * Validates a transaction hash format for Sui
569
+ */
570
+ static isTxHash(_v: unknown): _v is string {
571
+ return false
572
+ }
573
+
211
574
  /**
212
575
  * Gets the leaf hasher for Sui destination chains.
213
576
  * @param lane - Lane configuration.
@@ -219,7 +582,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
219
582
 
220
583
  /** {@inheritDoc Chain.getFee} */
221
584
  async getFee(_router: string, _destChainSelector: bigint, _message: AnyMessage): Promise<bigint> {
222
- return Promise.reject(new Error('Not implemented'))
585
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getFee'))
223
586
  }
224
587
 
225
588
  /** {@inheritDoc Chain.generateUnsignedSendMessage} */
@@ -230,7 +593,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
230
593
  _message: AnyMessage & { fee?: bigint },
231
594
  _opts?: { approveMax?: boolean },
232
595
  ): Promise<never> {
233
- return Promise.reject(new Error('Not implemented'))
596
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.generateUnsignedSendMessage'))
234
597
  }
235
598
 
236
599
  /** {@inheritDoc Chain.sendMessage} */
@@ -240,13 +603,13 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
240
603
  _message: AnyMessage & { fee: bigint },
241
604
  _opts?: { wallet?: unknown; approveMax?: boolean },
242
605
  ): Promise<CCIPRequest> {
243
- return Promise.reject(new Error('Not implemented'))
606
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.sendMessage'))
244
607
  }
245
608
 
246
609
  /** {@inheritDoc Chain.fetchOffchainTokenData} */
247
610
  fetchOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
248
611
  if (!('receiverObjectIds' in request.message)) {
249
- throw new Error('Invalid message, not v1.6 Sui')
612
+ throw new CCIPSuiMessageVersionInvalidError()
250
613
  }
251
614
  // default offchain token data
252
615
  return Promise.resolve(request.message.tokenAmounts.map(() => undefined))
@@ -259,16 +622,101 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
259
622
  _execReport: ExecutionReport,
260
623
  _opts: object,
261
624
  ): Promise<never> {
262
- return Promise.reject(new Error('Not implemented'))
625
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.generateUnsignedExecuteReport'))
263
626
  }
264
627
 
265
628
  /** {@inheritDoc Chain.executeReport} */
266
629
  async executeReport(
267
630
  _offRamp: string,
268
- _execReport: ExecutionReport,
269
- _opts?: { wallet?: unknown; gasLimit?: number },
631
+ execReport: ExecutionReport,
632
+ opts: { wallet: unknown; gasLimit?: number; receiverObjectIds?: string[] },
270
633
  ): Promise<ChainTransaction> {
271
- return Promise.reject(new Error('Not implemented'))
634
+ if (!this.contractsDir.offRamp || !this.contractsDir.ccip) {
635
+ throw new CCIPContractNotRouterError(
636
+ 'OffRamp or CCIP address not set in contracts directory',
637
+ 'Sui',
638
+ )
639
+ }
640
+ const wallet = opts.wallet as Keypair
641
+ const ccipObjectRef = await getCcipObjectRef(this.client, this.contractsDir.ccip)
642
+ const offrampStateObject = await getOffRampStateObject(this.client, this.contractsDir.offRamp)
643
+ const receiverConfig = await getReceiverModule(
644
+ this.client,
645
+ this.contractsDir.ccip,
646
+ ccipObjectRef,
647
+ execReport.message.receiver,
648
+ )
649
+ let tokenConfigs: TokenConfig[] = []
650
+ if (execReport.message.tokenAmounts.length !== 0) {
651
+ tokenConfigs = await fetchTokenConfigs(
652
+ this.client,
653
+ this.contractsDir.ccip,
654
+ ccipObjectRef,
655
+ execReport.message.tokenAmounts as CCIPMessage<typeof CCIPVersion.V1_6>['tokenAmounts'],
656
+ )
657
+ }
658
+
659
+ const input: SuiManuallyExecuteInput = {
660
+ executionReport: execReport as ExecutionReport<CCIPMessage_V1_6_Sui>,
661
+ offrampAddress: this.contractsDir.offRamp,
662
+ ccipAddress: this.contractsDir.ccip,
663
+ ccipObjectRef,
664
+ offrampStateObject,
665
+ receiverConfig,
666
+ tokenConfigs,
667
+ }
668
+ if (opts.receiverObjectIds) {
669
+ this.logger.info(
670
+ `Overriding Sui Manual Execution receiverObjectIds with: ${opts.receiverObjectIds.join(', ')}`,
671
+ )
672
+ input.overrideReceiverObjectIds = opts.receiverObjectIds
673
+ }
674
+ const tx = buildManualExecutionPTB(input)
675
+
676
+ // Set gas budget if provided
677
+ if (opts?.gasLimit) {
678
+ tx.setGasBudget(opts.gasLimit)
679
+ }
680
+
681
+ this.logger.info(`Executing Sui CCIP executeReport transaction...`)
682
+ // Sign and execute the transaction
683
+ let result: SuiTransactionBlockResponse
684
+ try {
685
+ result = await this.client.signAndExecuteTransaction({
686
+ signer: wallet,
687
+ transaction: tx,
688
+ options: {
689
+ showEffects: true,
690
+ showEvents: true,
691
+ },
692
+ })
693
+ } catch (e) {
694
+ throw new CCIPError(
695
+ CCIPErrorCode.TRANSACTION_NOT_FINALIZED,
696
+ `Failed to send Sui executeReport transaction: ${(e as Error).message}`,
697
+ )
698
+ }
699
+
700
+ // Check if transaction inmediately reverted
701
+ if (result.effects?.status?.status !== 'success') {
702
+ const errorMsg = result.effects?.status?.error || 'Unknown error'
703
+ throw new CCIPExecTxRevertedError(result.digest, {
704
+ context: { error: errorMsg },
705
+ })
706
+ }
707
+
708
+ this.logger.info(`Waiting for Sui transaction ${result.digest} to be finalized...`)
709
+
710
+ await this.client.waitForTransaction({
711
+ digest: result.digest,
712
+ options: {
713
+ showEffects: true,
714
+ showEvents: true,
715
+ },
716
+ })
717
+
718
+ // Return the transaction as a ChainTransaction
719
+ return this.getTransaction(result.digest)
272
720
  }
273
721
 
274
722
  /**
@@ -285,26 +733,26 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
285
733
 
286
734
  /** {@inheritDoc Chain.getSupportedTokens} */
287
735
  async getSupportedTokens(_address: string): Promise<string[]> {
288
- return Promise.reject(new Error('Not implemented'))
736
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getSupportedTokens'))
289
737
  }
290
738
 
291
739
  /** {@inheritDoc Chain.getRegistryTokenConfig} */
292
740
  async getRegistryTokenConfig(_address: string, _tokenName: string): Promise<never> {
293
- return Promise.reject(new Error('Not implemented'))
741
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getRegistryTokenConfig'))
294
742
  }
295
743
 
296
744
  /** {@inheritDoc Chain.getTokenPoolConfigs} */
297
745
  async getTokenPoolConfigs(_tokenPool: string): Promise<never> {
298
- return Promise.reject(new Error('Not implemented'))
746
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getTokenPoolConfigs'))
299
747
  }
300
748
 
301
749
  /** {@inheritDoc Chain.getTokenPoolRemotes} */
302
750
  async getTokenPoolRemotes(_tokenPool: string): Promise<never> {
303
- return Promise.reject(new Error('Not implemented'))
751
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getTokenPoolRemotes'))
304
752
  }
305
753
 
306
754
  /** {@inheritDoc Chain.getFeeTokens} */
307
755
  async getFeeTokens(_router: string): Promise<never> {
308
- return Promise.reject(new Error('Not implemented'))
756
+ return Promise.reject(new CCIPNotImplementedError('SuiChain.getFeeTokens'))
309
757
  }
310
758
  }