@chainlink/ccip-sdk 0.93.0 → 0.95.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.
- package/dist/api/index.d.ts +80 -4
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +262 -6
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +138 -13
- package/dist/api/types.d.ts.map +1 -1
- package/dist/aptos/index.d.ts +5 -9
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +26 -25
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.js +3 -3
- package/dist/aptos/logs.js.map +1 -1
- package/dist/aptos/send.js +1 -1
- package/dist/aptos/send.js.map +1 -1
- package/dist/chain.d.ts +96 -10
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +77 -2
- package/dist/chain.js.map +1 -1
- package/dist/errors/codes.d.ts +7 -3
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +8 -3
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +7 -7
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +7 -7
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +8 -4
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +53 -18
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +112 -37
- package/dist/errors/specialized.js.map +1 -1
- package/dist/evm/gas.d.ts +14 -0
- package/dist/evm/gas.d.ts.map +1 -0
- package/dist/evm/gas.js +97 -0
- package/dist/evm/gas.js.map +1 -0
- package/dist/evm/index.d.ts +6 -8
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +36 -23
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +8 -8
- package/dist/evm/offchain.js.map +1 -1
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +8 -1
- package/dist/execution.js.map +1 -1
- package/dist/gas.d.ts +43 -19
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +48 -68
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts +5 -4
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +7 -6
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +21 -13
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +79 -47
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts +2 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +629 -274
- package/dist/selectors.js.map +1 -1
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +2 -1
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/index.d.ts +10 -10
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +82 -18
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.js +2 -2
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +6 -9
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/utils.d.ts +29 -1
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +39 -1
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/discovery.d.ts +7 -4
- package/dist/sui/discovery.d.ts.map +1 -1
- package/dist/sui/discovery.js +66 -19
- package/dist/sui/discovery.js.map +1 -1
- package/dist/sui/events.d.ts +23 -12
- package/dist/sui/events.d.ts.map +1 -1
- package/dist/sui/events.js +267 -128
- package/dist/sui/events.js.map +1 -1
- package/dist/sui/index.d.ts +32 -39
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +289 -163
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
- package/dist/sui/manuallyExec/encoder.js +1 -0
- package/dist/sui/manuallyExec/encoder.js.map +1 -1
- package/dist/sui/manuallyExec/index.d.ts.map +1 -1
- package/dist/sui/manuallyExec/index.js +1 -0
- package/dist/sui/manuallyExec/index.js.map +1 -1
- package/dist/sui/objects.d.ts +14 -4
- package/dist/sui/objects.d.ts.map +1 -1
- package/dist/sui/objects.js +63 -69
- package/dist/sui/objects.js.map +1 -1
- package/dist/sui/types.d.ts +33 -0
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js.map +1 -1
- package/dist/ton/hasher.d.ts.map +1 -1
- package/dist/ton/hasher.js +1 -0
- package/dist/ton/hasher.js.map +1 -1
- package/dist/ton/index.d.ts +4 -4
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +8 -8
- package/dist/ton/index.js.map +1 -1
- package/dist/ton/utils.d.ts +3 -3
- package/dist/ton/utils.d.ts.map +1 -1
- package/dist/ton/utils.js +6 -5
- package/dist/ton/utils.js.map +1 -1
- package/dist/types.d.ts +34 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +19 -5
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +53 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +109 -12
- package/dist/utils.js.map +1 -1
- package/package.json +17 -11
- package/src/api/index.ts +343 -9
- package/src/api/types.ts +165 -13
- package/src/aptos/index.ts +32 -32
- package/src/aptos/logs.ts +3 -3
- package/src/aptos/send.ts +1 -1
- package/src/chain.ts +165 -12
- package/src/errors/codes.ts +8 -3
- package/src/errors/index.ts +7 -4
- package/src/errors/recovery.ts +16 -5
- package/src/errors/specialized.ts +147 -45
- package/src/evm/gas.ts +149 -0
- package/src/evm/index.ts +66 -33
- package/src/evm/offchain.ts +15 -9
- package/src/execution.ts +8 -1
- package/src/gas.ts +95 -116
- package/src/index.ts +16 -6
- package/src/offchain.ts +12 -6
- package/src/requests.ts +113 -59
- package/src/selectors.ts +636 -276
- package/src/solana/exec.ts +3 -1
- package/src/solana/index.ts +119 -23
- package/src/solana/offchain.ts +2 -2
- package/src/solana/send.ts +5 -23
- package/src/solana/utils.ts +66 -0
- package/src/sui/discovery.ts +92 -31
- package/src/sui/events.ts +346 -239
- package/src/sui/index.ts +381 -224
- package/src/sui/manuallyExec/encoder.ts +2 -0
- package/src/sui/manuallyExec/index.ts +2 -0
- package/src/sui/objects.ts +77 -99
- package/src/sui/types.ts +35 -0
- package/src/ton/hasher.ts +2 -0
- package/src/ton/index.ts +12 -11
- package/src/ton/utils.ts +7 -6
- package/src/types.ts +36 -10
- package/src/utils.ts +153 -16
package/src/sui/index.ts
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bcs } from '@mysten/sui/bcs'
|
|
2
2
|
import { type SuiTransactionBlockResponse, SuiClient } from '@mysten/sui/client'
|
|
3
3
|
import type { Keypair } from '@mysten/sui/cryptography'
|
|
4
4
|
import { SuiGraphQLClient } from '@mysten/sui/graphql'
|
|
5
5
|
import { Transaction } from '@mysten/sui/transactions'
|
|
6
|
-
import {
|
|
7
|
-
import type
|
|
6
|
+
import { isValidSuiAddress, isValidTransactionDigest, normalizeSuiAddress } from '@mysten/sui/utils'
|
|
7
|
+
import { type BytesLike, dataLength, hexlify, isBytesLike, isHexString } from 'ethers'
|
|
8
|
+
import type { PickDeep, SetOptional } from 'type-fest'
|
|
8
9
|
|
|
9
10
|
import { AptosChain } from '../aptos/index.ts'
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type ChainContext,
|
|
13
|
+
type ChainStatic,
|
|
14
|
+
type GetBalanceOpts,
|
|
15
|
+
type LogFilter,
|
|
16
|
+
Chain,
|
|
17
|
+
} from '../chain.ts'
|
|
11
18
|
import {
|
|
12
19
|
CCIPContractNotRouterError,
|
|
13
20
|
CCIPDataFormatUnsupportedError,
|
|
14
21
|
CCIPError,
|
|
15
22
|
CCIPErrorCode,
|
|
16
23
|
CCIPExecTxRevertedError,
|
|
17
|
-
CCIPExtraArgsInvalidError,
|
|
18
24
|
CCIPNotImplementedError,
|
|
19
|
-
CCIPSuiMessageVersionInvalidError,
|
|
20
|
-
CCIPVersionFeatureUnavailableError,
|
|
21
25
|
} from '../errors/index.ts'
|
|
22
|
-
import
|
|
23
|
-
|
|
26
|
+
import {
|
|
27
|
+
CCIPLogsAddressRequiredError,
|
|
28
|
+
CCIPSuiLogInvalidError,
|
|
29
|
+
CCIPTopicsInvalidError,
|
|
30
|
+
} from '../errors/specialized.ts'
|
|
31
|
+
import type { EVMExtraArgsV2, ExtraArgs, SVMExtraArgsV1, SuiExtraArgsV1 } from '../extra-args.ts'
|
|
24
32
|
import type { LeafHasher } from '../hasher/common.ts'
|
|
33
|
+
import { decodeMessage, getMessagesInBatch } from '../requests.ts'
|
|
25
34
|
import { supportedChains } from '../supported-chains.ts'
|
|
35
|
+
import { getSuiLeafHasher } from './hasher.ts'
|
|
26
36
|
import {
|
|
37
|
+
type AnyMessage,
|
|
27
38
|
type CCIPExecution,
|
|
28
39
|
type CCIPMessage,
|
|
29
40
|
type CCIPRequest,
|
|
@@ -40,30 +51,31 @@ import {
|
|
|
40
51
|
type WithLogger,
|
|
41
52
|
ChainFamily,
|
|
42
53
|
} from '../types.ts'
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
import {
|
|
55
|
+
decodeAddress,
|
|
56
|
+
decodeOnRampAddress,
|
|
57
|
+
getDataBytes,
|
|
58
|
+
networkInfo,
|
|
59
|
+
parseTypeAndVersion,
|
|
60
|
+
util,
|
|
61
|
+
} from '../utils.ts'
|
|
62
|
+
import { getCcipStateAddress, getOffRampForCcip } from './discovery.ts'
|
|
63
|
+
import { type CommitEvent, streamSuiLogs } from './events.ts'
|
|
47
64
|
import {
|
|
48
65
|
type SuiManuallyExecuteInput,
|
|
49
66
|
type TokenConfig,
|
|
50
67
|
buildManualExecutionPTB,
|
|
51
68
|
} from './manuallyExec/index.ts'
|
|
52
69
|
import {
|
|
70
|
+
deriveObjectID,
|
|
53
71
|
fetchTokenConfigs,
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
getLatestPackageId,
|
|
73
|
+
getObjectRef,
|
|
56
74
|
getReceiverModule,
|
|
57
75
|
} from './objects.ts'
|
|
76
|
+
import type { CCIPMessage_V1_6_Sui } from './types.ts'
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
type SuiContractDir = {
|
|
62
|
-
ccip?: string
|
|
63
|
-
onRamp?: string
|
|
64
|
-
offRamp?: string
|
|
65
|
-
router?: string
|
|
66
|
-
}
|
|
78
|
+
const DEFAULT_GAS_LIMIT = 1000000n
|
|
67
79
|
|
|
68
80
|
/**
|
|
69
81
|
* Sui chain implementation supporting Sui networks.
|
|
@@ -80,9 +92,6 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
80
92
|
readonly client: SuiClient
|
|
81
93
|
readonly graphqlClient: SuiGraphQLClient
|
|
82
94
|
|
|
83
|
-
// contracts dir <chainSelectorName, SuiContractDir>
|
|
84
|
-
readonly contractsDir: SuiContractDir
|
|
85
|
-
|
|
86
95
|
/**
|
|
87
96
|
* Creates a new SuiChain instance.
|
|
88
97
|
* @param client - Sui client for interacting with the Sui network.
|
|
@@ -93,7 +102,6 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
93
102
|
|
|
94
103
|
this.client = client
|
|
95
104
|
this.network = network
|
|
96
|
-
this.contractsDir = {}
|
|
97
105
|
|
|
98
106
|
// TODO: Graphql client should come from config
|
|
99
107
|
let graphqlUrl: string
|
|
@@ -138,17 +146,19 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
138
146
|
chainId = 'sui:4' // devnet
|
|
139
147
|
} else {
|
|
140
148
|
throw new CCIPError(
|
|
141
|
-
CCIPErrorCode.
|
|
149
|
+
CCIPErrorCode.CHAIN_FAMILY_UNSUPPORTED,
|
|
142
150
|
`Unsupported Sui chain identifier: ${rawChainId}`,
|
|
143
151
|
)
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
const network = networkInfo(chainId) as NetworkInfo<typeof ChainFamily.Sui>
|
|
147
|
-
|
|
155
|
+
const chain = new SuiChain(client, network, ctx)
|
|
156
|
+
return Object.assign(chain, { url })
|
|
148
157
|
}
|
|
149
158
|
|
|
150
159
|
/** {@inheritDoc Chain.getBlockTimestamp} */
|
|
151
|
-
async getBlockTimestamp(block: number): Promise<number> {
|
|
160
|
+
async getBlockTimestamp(block: number | 'finalized'): Promise<number> {
|
|
161
|
+
if (typeof block !== 'number' || block <= 0) return Math.floor(Date.now() / 1000)
|
|
152
162
|
const checkpoint = await this.client.getCheckpoint({
|
|
153
163
|
id: String(block),
|
|
154
164
|
})
|
|
@@ -174,12 +184,12 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
174
184
|
if (txResponse.events?.length) {
|
|
175
185
|
for (const [i, event] of txResponse.events.entries()) {
|
|
176
186
|
const eventType = event.type
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const eventName = eventType.
|
|
187
|
+
const splitIdx = eventType.lastIndexOf('::')
|
|
188
|
+
const address = eventType.substring(0, splitIdx)
|
|
189
|
+
const eventName = eventType.substring(splitIdx + 2)
|
|
180
190
|
|
|
181
191
|
events.push({
|
|
182
|
-
address:
|
|
192
|
+
address: address,
|
|
183
193
|
transactionHash: digest,
|
|
184
194
|
index: i,
|
|
185
195
|
blockNumber: Number(txResponse.checkpoint || 0),
|
|
@@ -200,41 +210,21 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
200
210
|
|
|
201
211
|
/** {@inheritDoc Chain.getLogs} */
|
|
202
212
|
async *getLogs(opts: LogFilter & { versionAsHash?: boolean }) {
|
|
203
|
-
if (!
|
|
204
|
-
|
|
205
|
-
}
|
|
213
|
+
if (!opts.address) throw new CCIPLogsAddressRequiredError()
|
|
214
|
+
|
|
206
215
|
// Extract the event type from topics
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
throw new CCIPVersionFeatureUnavailableError(
|
|
210
|
-
'Event type',
|
|
211
|
-
topic || 'unknown',
|
|
212
|
-
'CommitReportAccepted',
|
|
213
|
-
)
|
|
216
|
+
if (opts.topics?.length !== 1 || typeof opts.topics[0] !== 'string') {
|
|
217
|
+
throw new CCIPTopicsInvalidError(opts.topics!)
|
|
214
218
|
}
|
|
219
|
+
const topic = opts.topics[0]
|
|
215
220
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
: new Date(startTime.getTime() + 1 * 24 * 60 * 60 * 1000) // default to +24h
|
|
220
|
-
|
|
221
|
-
this.logger.info(
|
|
222
|
-
`Fetching Sui events of type ${topic} from ${startTime.toISOString()} to ${endTime.toISOString()}`,
|
|
223
|
-
)
|
|
224
|
-
const events = await getSuiEventsInTimeRange<CommitEvent>(
|
|
225
|
-
this.client,
|
|
226
|
-
this.graphqlClient,
|
|
227
|
-
`${this.contractsDir.offRamp}::offramp::CommitReportAccepted`,
|
|
228
|
-
startTime,
|
|
229
|
-
endTime,
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
for (const event of events) {
|
|
233
|
-
const eventData = event.contents.json
|
|
221
|
+
for await (const event of streamSuiLogs<Record<string, unknown>>(this, opts)) {
|
|
222
|
+
const eventData = event.contents?.json
|
|
223
|
+
if (!eventData) continue
|
|
234
224
|
yield {
|
|
235
|
-
address:
|
|
236
|
-
transactionHash: event.transaction
|
|
237
|
-
index:
|
|
225
|
+
address: opts.address,
|
|
226
|
+
transactionHash: event.transaction!.digest,
|
|
227
|
+
index: Number(event.sequenceNumber) || 0,
|
|
238
228
|
blockNumber: Number(event.transaction?.effects.checkpoint.sequenceNumber || 0),
|
|
239
229
|
data: eventData,
|
|
240
230
|
topics: [topic],
|
|
@@ -242,11 +232,6 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
242
232
|
}
|
|
243
233
|
}
|
|
244
234
|
|
|
245
|
-
/** {@inheritDoc Chain.getMessagesInTx} */
|
|
246
|
-
override async getMessagesInTx(_tx: string | ChainTransaction): Promise<CCIPRequest[]> {
|
|
247
|
-
return Promise.reject(new CCIPNotImplementedError('SuiChain.getMessagesInTx'))
|
|
248
|
-
}
|
|
249
|
-
|
|
250
235
|
/** {@inheritDoc Chain.getMessagesInBatch} */
|
|
251
236
|
override async getMessagesInBatch<
|
|
252
237
|
R extends PickDeep<
|
|
@@ -254,25 +239,45 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
254
239
|
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
|
|
255
240
|
>,
|
|
256
241
|
>(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
242
|
+
request: R,
|
|
243
|
+
commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
|
|
244
|
+
opts?: { page?: number },
|
|
260
245
|
): Promise<R['message'][]> {
|
|
261
|
-
return
|
|
246
|
+
return getMessagesInBatch(this, request, commit, opts)
|
|
262
247
|
}
|
|
263
248
|
|
|
264
249
|
/** {@inheritDoc Chain.typeAndVersion} */
|
|
265
|
-
async typeAndVersion(
|
|
266
|
-
|
|
250
|
+
async typeAndVersion(address: string) {
|
|
251
|
+
// requires address to have `::<module>` suffix
|
|
252
|
+
address = await getLatestPackageId(address, this.client)
|
|
253
|
+
const target = `${address}::type_and_version`
|
|
254
|
+
|
|
255
|
+
// Use the Transaction builder to create a move call
|
|
256
|
+
const tx = new Transaction()
|
|
257
|
+
// Add move call to the transaction
|
|
258
|
+
tx.moveCall({ target, arguments: [] })
|
|
259
|
+
|
|
260
|
+
// Execute with devInspectTransactionBlock for read-only call
|
|
261
|
+
const result = await this.client.devInspectTransactionBlock({
|
|
262
|
+
sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
263
|
+
transactionBlock: tx,
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
if (result.effects.status.status !== 'success' || !result.results?.[0]?.returnValues?.[0]) {
|
|
267
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
268
|
+
`Failed to call ${target}: ${result.effects.status.error || 'No return value'}`,
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const [data] = result.results[0].returnValues[0]
|
|
273
|
+
const res = bcs.String.parse(getDataBytes(data))
|
|
274
|
+
return parseTypeAndVersion(res)
|
|
267
275
|
}
|
|
268
276
|
|
|
269
277
|
/** {@inheritDoc Chain.getRouterForOnRamp} */
|
|
270
278
|
async getRouterForOnRamp(onRamp: string, _destChainSelector: bigint): Promise<string> {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.contractsDir.onRamp = onRamp
|
|
274
|
-
}
|
|
275
|
-
return Promise.resolve(this.contractsDir.onRamp)
|
|
279
|
+
// In Sui, the router is the onRamp package itself
|
|
280
|
+
return Promise.resolve(onRamp)
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
/** {@inheritDoc Chain.getRouterForOffRamp} */
|
|
@@ -281,40 +286,40 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
/** {@inheritDoc Chain.getNativeTokenForRouter} */
|
|
284
|
-
getNativeTokenForRouter(
|
|
289
|
+
getNativeTokenForRouter(): Promise<string> {
|
|
285
290
|
// SUI native token is always 0x2::sui::SUI
|
|
286
291
|
return Promise.resolve('0x2::sui::SUI')
|
|
287
292
|
}
|
|
288
293
|
|
|
289
294
|
/** {@inheritDoc Chain.getOffRampsForRouter} */
|
|
290
295
|
async getOffRampsForRouter(router: string, _sourceChainSelector: bigint): Promise<string[]> {
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
this.contractsDir.ccip = ccip
|
|
296
|
+
router = await getLatestPackageId(router, this.client)
|
|
297
|
+
const ccip = await getCcipStateAddress(router, this.client)
|
|
298
|
+
const offramp = await getOffRampForCcip(ccip, this.client)
|
|
295
299
|
return [offramp]
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
/** {@inheritDoc Chain.getOnRampForRouter} */
|
|
299
|
-
getOnRampForRouter(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
return Promise.resolve(this.contractsDir.onRamp)
|
|
303
|
+
getOnRampForRouter(router: string, _destChainSelector: bigint): Promise<string> {
|
|
304
|
+
// For Sui, the router is the onramp package address
|
|
305
|
+
return Promise.resolve(router)
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
/** {@inheritDoc Chain.getOnRampForOffRamp} */
|
|
307
309
|
async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
308
|
-
|
|
309
|
-
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'CCIP address not set in contracts directory')
|
|
310
|
-
}
|
|
311
|
-
const offrampPackageId = offRamp
|
|
310
|
+
offRamp = await getLatestPackageId(offRamp, this.client)
|
|
312
311
|
const functionName = 'get_source_chain_config'
|
|
313
|
-
|
|
312
|
+
// Preserve module suffix if present, otherwise add it
|
|
313
|
+
const target = offRamp.includes('::')
|
|
314
|
+
? `${offRamp}::${functionName}`
|
|
315
|
+
: `${offRamp}::offramp::${functionName}`
|
|
316
|
+
|
|
317
|
+
// Discover the CCIP package from the offramp
|
|
318
|
+
const ccip = await getCcipStateAddress(offRamp, this.client)
|
|
314
319
|
|
|
315
320
|
// Get the OffRampState object
|
|
316
|
-
const offrampStateObject = await
|
|
317
|
-
const ccipObjectRef = await
|
|
321
|
+
const offrampStateObject = await getObjectRef(offRamp, this.client)
|
|
322
|
+
const ccipObjectRef = await getObjectRef(ccip, this.client)
|
|
318
323
|
// Use the Transaction builder to create a move call
|
|
319
324
|
const tx = new Transaction()
|
|
320
325
|
|
|
@@ -379,43 +384,170 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
379
384
|
}
|
|
380
385
|
|
|
381
386
|
/** {@inheritDoc Chain.getTokenForTokenPool} */
|
|
382
|
-
getTokenForTokenPool(
|
|
383
|
-
|
|
387
|
+
async getTokenForTokenPool(tokenPool: string): Promise<string> {
|
|
388
|
+
const normalizedTokenPool = normalizeSuiAddress(tokenPool)
|
|
389
|
+
|
|
390
|
+
// Get objects owned by this package (looking for state pointers)
|
|
391
|
+
const objects = await this.client.getOwnedObjects({
|
|
392
|
+
owner: normalizedTokenPool,
|
|
393
|
+
options: { showType: true, showContent: true },
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
const tpType = objects.data
|
|
397
|
+
.find((obj) => obj.data?.type?.includes('token_pool::'))
|
|
398
|
+
?.data?.type?.split('::')[1]
|
|
399
|
+
|
|
400
|
+
const allowedTps = ['managed_token_pool', 'burn_mint_token_pool', 'lock_release_token_pool']
|
|
401
|
+
if (!tpType || !allowedTps.includes(tpType)) {
|
|
402
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, `Invalid token pool type: ${tpType}`)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Find the state pointer object
|
|
406
|
+
let stateObjectPointerId: string | undefined
|
|
407
|
+
for (const obj of objects.data) {
|
|
408
|
+
const content = obj.data?.content
|
|
409
|
+
if (content?.dataType !== 'moveObject') continue
|
|
410
|
+
|
|
411
|
+
const fields = content.fields as Record<string, unknown>
|
|
412
|
+
// Look for a pointer field that references the state object
|
|
413
|
+
stateObjectPointerId = fields[`${tpType}_object_id`] as string
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!stateObjectPointerId) {
|
|
417
|
+
throw new CCIPError(
|
|
418
|
+
CCIPErrorCode.UNKNOWN,
|
|
419
|
+
`No token pool state pointer found for ${tokenPool}`,
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const stateNamesPerTP: Record<string, string> = {
|
|
424
|
+
managed_token_pool: 'ManagedTokenPoolState',
|
|
425
|
+
burn_mint_token_pool: 'BurnMintTokenPoolState',
|
|
426
|
+
lock_release_token_pool: 'LockReleaseTokenPoolState',
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const poolStateObject = deriveObjectID(
|
|
430
|
+
stateObjectPointerId,
|
|
431
|
+
new TextEncoder().encode(stateNamesPerTP[tpType]),
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
// Get object info to get the coin type
|
|
435
|
+
const info = await this.client.getObject({
|
|
436
|
+
id: poolStateObject,
|
|
437
|
+
options: { showType: true, showContent: true },
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
const type = info.data?.type
|
|
441
|
+
if (!type) {
|
|
442
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Error loading token pool state object type')
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Extract the type parameter T from ManagedTokenPoolState<T>
|
|
446
|
+
const typeMatch = type.match(/(?:Managed|BurnMint|LockRelease)TokenPoolState<(.+)>$/)
|
|
447
|
+
if (!typeMatch || !typeMatch[1]) {
|
|
448
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, `Invalid pool state type format: ${type}`)
|
|
449
|
+
}
|
|
450
|
+
const tokenType = typeMatch[1]
|
|
451
|
+
|
|
452
|
+
// Call get_token function from managed_token_pool contract with the type parameter
|
|
453
|
+
const target = type.split('<')[0]?.split('::').slice(0, 2).join('::') + '::get_token'
|
|
454
|
+
if (!target) {
|
|
455
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, `Invalid pool state type format: ${type}`)
|
|
456
|
+
}
|
|
457
|
+
const tx = new Transaction()
|
|
458
|
+
tx.moveCall({
|
|
459
|
+
target,
|
|
460
|
+
typeArguments: [tokenType],
|
|
461
|
+
arguments: [tx.object(poolStateObject)],
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
const result = await this.client.devInspectTransactionBlock({
|
|
465
|
+
sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
466
|
+
transactionBlock: tx,
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
if (result.effects.status.status !== 'success' || !result.results?.[0]?.returnValues?.[0]) {
|
|
470
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
471
|
+
`Failed to call ${target}: ${result.effects.status.error || 'No return value'}`,
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Parse the return value to get the coin metadata address (32 bytes)
|
|
476
|
+
const returnValue = result.results[0].returnValues[0]
|
|
477
|
+
const [data] = returnValue
|
|
478
|
+
const coinMetadataBytes = new Uint8Array(data)
|
|
479
|
+
const coinMetadataAddress = normalizeSuiAddress(hexlify(coinMetadataBytes))
|
|
480
|
+
|
|
481
|
+
return coinMetadataAddress
|
|
384
482
|
}
|
|
385
483
|
|
|
386
484
|
/** {@inheritDoc Chain.getTokenInfo} */
|
|
387
485
|
async getTokenInfo(token: string): Promise<{ symbol: string; decimals: number }> {
|
|
388
|
-
|
|
389
|
-
if (
|
|
390
|
-
|
|
486
|
+
const normalizedTokenAddress = normalizeSuiAddress(token)
|
|
487
|
+
if (!isValidSuiAddress(normalizedTokenAddress)) {
|
|
488
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Error loading Sui token metadata')
|
|
391
489
|
}
|
|
392
490
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
491
|
+
const objectResponse = await this.client.getObject({
|
|
492
|
+
id: normalizedTokenAddress,
|
|
493
|
+
options: { showType: true },
|
|
494
|
+
})
|
|
397
495
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
496
|
+
const getCoinFromMetadata = (metadata: string) => {
|
|
497
|
+
// Extract the type parameter from CoinMetadata<...>
|
|
498
|
+
const match = metadata.match(/CoinMetadata<(.+)>$/)
|
|
499
|
+
|
|
500
|
+
if (!match || !match[1]) {
|
|
501
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, `Invalid metadata format: ${metadata}`)
|
|
403
502
|
}
|
|
404
|
-
|
|
405
|
-
|
|
503
|
+
|
|
504
|
+
return match[1]
|
|
406
505
|
}
|
|
407
506
|
|
|
408
|
-
|
|
409
|
-
const
|
|
410
|
-
|
|
507
|
+
let coinType: string
|
|
508
|
+
const objectType = objectResponse.data?.type
|
|
509
|
+
|
|
510
|
+
// Check if this is a CoinMetadata object or a coin type string
|
|
511
|
+
if (objectType?.includes('CoinMetadata')) {
|
|
512
|
+
coinType = getCoinFromMetadata(objectType)
|
|
513
|
+
} else if (token.includes('::')) {
|
|
514
|
+
// This is a coin type string (e.g., "0xabc::coin::COIN")
|
|
515
|
+
coinType = token
|
|
516
|
+
} else {
|
|
517
|
+
// This is a package address or unknown format
|
|
518
|
+
throw new CCIPError(
|
|
519
|
+
CCIPErrorCode.UNKNOWN,
|
|
520
|
+
`Token address ${token} is not a CoinMetadata object or coin type. Expected format: package::module::Type`,
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (coinType.split('::').length < 3) {
|
|
525
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Error loading Sui token metadata')
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
let metadata = null
|
|
529
|
+
try {
|
|
530
|
+
metadata = await this.client.getCoinMetadata({ coinType })
|
|
531
|
+
} catch (e) {
|
|
532
|
+
console.error('Error fetching coin metadata:', e)
|
|
533
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Error loading Sui token metadata')
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (!metadata) {
|
|
537
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Error loading Sui token metadata')
|
|
538
|
+
}
|
|
411
539
|
|
|
412
540
|
return {
|
|
413
|
-
symbol: symbol
|
|
414
|
-
decimals:
|
|
541
|
+
symbol: metadata.symbol,
|
|
542
|
+
decimals: metadata.decimals,
|
|
415
543
|
}
|
|
416
544
|
}
|
|
417
545
|
|
|
418
|
-
/** {@inheritDoc Chain.
|
|
546
|
+
/** {@inheritDoc Chain.getBalance} */
|
|
547
|
+
async getBalance(_opts: GetBalanceOpts): Promise<bigint> {
|
|
548
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getBalance'))
|
|
549
|
+
}
|
|
550
|
+
|
|
419
551
|
/** {@inheritDoc Chain.getTokenAdminRegistryFor} */
|
|
420
552
|
getTokenAdminRegistryFor(_address: string): Promise<string> {
|
|
421
553
|
return Promise.reject(new CCIPNotImplementedError())
|
|
@@ -424,11 +556,22 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
424
556
|
// Static methods for decoding
|
|
425
557
|
/**
|
|
426
558
|
* Decodes a CCIP message from a Sui log event.
|
|
427
|
-
* @param
|
|
559
|
+
* @param log - Log event data.
|
|
428
560
|
* @returns Decoded CCIPMessage or undefined if not valid.
|
|
429
561
|
*/
|
|
430
|
-
static decodeMessage(
|
|
431
|
-
|
|
562
|
+
static decodeMessage(log: Log_): CCIPMessage | undefined {
|
|
563
|
+
const { data } = log
|
|
564
|
+
if (
|
|
565
|
+
(typeof data !== 'string' || !data.startsWith('{')) &&
|
|
566
|
+
(typeof data !== 'object' || isBytesLike(data))
|
|
567
|
+
)
|
|
568
|
+
throw new CCIPSuiLogInvalidError(util.inspect(log))
|
|
569
|
+
// offload massaging to generic decodeJsonMessage
|
|
570
|
+
try {
|
|
571
|
+
return decodeMessage(data)
|
|
572
|
+
} catch (_) {
|
|
573
|
+
// return undefined
|
|
574
|
+
}
|
|
432
575
|
}
|
|
433
576
|
|
|
434
577
|
/**
|
|
@@ -438,28 +581,11 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
438
581
|
*/
|
|
439
582
|
static decodeExtraArgs(
|
|
440
583
|
extraArgs: BytesLike,
|
|
441
|
-
):
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const abiData = '0x' + hexBytes.slice(8)
|
|
449
|
-
const decoded = AbiCoder.defaultAbiCoder().decode(
|
|
450
|
-
['tuple(uint256,bool,bytes32,bytes32[])'],
|
|
451
|
-
abiData,
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
const tuple = decoded[0] as readonly [bigint, boolean, string, string[]]
|
|
455
|
-
|
|
456
|
-
return {
|
|
457
|
-
gasLimit: tuple[0],
|
|
458
|
-
allowOutOfOrderExecution: tuple[1],
|
|
459
|
-
tokenReceiver: tuple[2],
|
|
460
|
-
receiverObjectIds: tuple[3], // Already an array of hex strings
|
|
461
|
-
_tag: 'SuiExtraArgsV1',
|
|
462
|
-
}
|
|
584
|
+
):
|
|
585
|
+
| (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
|
|
586
|
+
| (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
|
|
587
|
+
| undefined {
|
|
588
|
+
return AptosChain.decodeExtraArgs(extraArgs)
|
|
463
589
|
}
|
|
464
590
|
|
|
465
591
|
/**
|
|
@@ -474,30 +600,36 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
474
600
|
/**
|
|
475
601
|
* Decodes commit reports from a log entry.
|
|
476
602
|
* @param log - The log entry to decode.
|
|
477
|
-
* @param
|
|
603
|
+
* @param lane - Optional lane information.
|
|
478
604
|
* @returns Array of decoded commit reports or undefined.
|
|
479
605
|
*/
|
|
480
|
-
static decodeCommits(
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (!
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
return
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
606
|
+
static decodeCommits(
|
|
607
|
+
{ data, topics }: SetOptional<Pick<Log_, 'data' | 'topics'>, 'topics'>,
|
|
608
|
+
lane?: Lane,
|
|
609
|
+
): CommitReport[] | undefined {
|
|
610
|
+
// Check if this is an CommitReportAccepted event
|
|
611
|
+
if (topics?.[0] && topics[0] !== 'CommitReportAccepted') return
|
|
612
|
+
|
|
613
|
+
// Basic log data structure validation
|
|
614
|
+
if (!data || typeof data !== 'object' || !('unblessed_merkle_roots' in data)) return
|
|
615
|
+
|
|
616
|
+
const eventData = data as CommitEvent
|
|
617
|
+
const rootsRaw = eventData.blessed_merkle_roots.concat(eventData.unblessed_merkle_roots)
|
|
618
|
+
return rootsRaw
|
|
619
|
+
.map((root) => {
|
|
620
|
+
return {
|
|
621
|
+
sourceChainSelector: BigInt(root.source_chain_selector),
|
|
622
|
+
onRampAddress: decodeOnRampAddress(root.on_ramp_address),
|
|
623
|
+
minSeqNr: BigInt(root.min_seq_nr),
|
|
624
|
+
maxSeqNr: BigInt(root.max_seq_nr),
|
|
625
|
+
merkleRoot: hexlify(getDataBytes(root.merkle_root)),
|
|
626
|
+
}
|
|
627
|
+
})
|
|
628
|
+
.filter((r) =>
|
|
629
|
+
lane
|
|
630
|
+
? r.sourceChainSelector === lane.sourceChainSelector && r.onRampAddress === lane.onRamp
|
|
631
|
+
: true,
|
|
632
|
+
)
|
|
501
633
|
}
|
|
502
634
|
|
|
503
635
|
/**
|
|
@@ -505,52 +637,32 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
505
637
|
* @param log - The log entry to decode.
|
|
506
638
|
* @returns Decoded execution receipt or undefined.
|
|
507
639
|
*/
|
|
508
|
-
static decodeReceipt(
|
|
640
|
+
static decodeReceipt({
|
|
641
|
+
data,
|
|
642
|
+
topics,
|
|
643
|
+
}: SetOptional<Pick<Log_, 'data' | 'topics'>, 'topics'>): ExecutionReceipt | undefined {
|
|
509
644
|
// Check if this is an ExecutionStateChanged event
|
|
510
|
-
|
|
511
|
-
if (topic !== 'ExecutionStateChanged') {
|
|
512
|
-
return undefined
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// Validate log data structure
|
|
516
|
-
if (!log.data || typeof log.data !== 'object') {
|
|
517
|
-
return undefined
|
|
518
|
-
}
|
|
645
|
+
if (topics?.[0] && topics[0] !== 'ExecutionStateChanged') return
|
|
519
646
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
sequence_number?: string
|
|
524
|
-
source_chain_selector?: string
|
|
525
|
-
state?: number
|
|
647
|
+
// Basic log data structure validation
|
|
648
|
+
if (!data || typeof data !== 'object' || !('message_id' in data) || !('state' in data)) {
|
|
649
|
+
return
|
|
526
650
|
}
|
|
527
651
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
) {
|
|
535
|
-
return undefined
|
|
652
|
+
const eventData = data as {
|
|
653
|
+
message_hash: BytesLike
|
|
654
|
+
message_id: BytesLike
|
|
655
|
+
sequence_number: string
|
|
656
|
+
source_chain_selector: string
|
|
657
|
+
state: number
|
|
536
658
|
}
|
|
537
659
|
|
|
538
|
-
const toHex = (bytes: BytesLike | number[]) => hexlify(bytesToBuffer(bytes))
|
|
539
|
-
|
|
540
|
-
// Convert message_id bytes array to hex string
|
|
541
|
-
const messageId = toHex(eventData.message_id)
|
|
542
|
-
|
|
543
|
-
// Convert message_hash bytes array to hex string (if present)
|
|
544
|
-
const messageHash = eventData.message_hash ? toHex(eventData.message_hash) : undefined
|
|
545
|
-
|
|
546
660
|
return {
|
|
547
|
-
messageId,
|
|
661
|
+
messageId: hexlify(getDataBytes(eventData.message_id)),
|
|
548
662
|
sequenceNumber: BigInt(eventData.sequence_number),
|
|
549
|
-
state: eventData.state as ExecutionState,
|
|
550
|
-
sourceChainSelector: eventData.source_chain_selector
|
|
551
|
-
|
|
552
|
-
: undefined,
|
|
553
|
-
messageHash,
|
|
663
|
+
state: Number(eventData.state) as ExecutionState,
|
|
664
|
+
sourceChainSelector: BigInt(eventData.source_chain_selector),
|
|
665
|
+
messageHash: hexlify(getDataBytes(eventData.message_hash)),
|
|
554
666
|
}
|
|
555
667
|
}
|
|
556
668
|
|
|
@@ -559,15 +671,17 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
559
671
|
* @param bytes - Bytes to convert.
|
|
560
672
|
* @returns Sui address.
|
|
561
673
|
*/
|
|
562
|
-
static getAddress(bytes: BytesLike): string {
|
|
674
|
+
static getAddress(bytes: BytesLike | readonly number[]): string {
|
|
563
675
|
return AptosChain.getAddress(bytes)
|
|
564
676
|
}
|
|
565
677
|
|
|
566
678
|
/**
|
|
567
679
|
* Validates a transaction hash format for Sui
|
|
568
680
|
*/
|
|
569
|
-
static isTxHash(
|
|
570
|
-
return false
|
|
681
|
+
static isTxHash(v: unknown): v is string {
|
|
682
|
+
if (typeof v !== 'string') return false
|
|
683
|
+
// check in both hex and base58 formats
|
|
684
|
+
return isHexString(v, 32) || isValidTransactionDigest(v)
|
|
571
685
|
}
|
|
572
686
|
|
|
573
687
|
/**
|
|
@@ -598,9 +712,6 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
598
712
|
|
|
599
713
|
/** {@inheritDoc Chain.getOffchainTokenData} */
|
|
600
714
|
getOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
|
|
601
|
-
if (!('receiverObjectIds' in request.message)) {
|
|
602
|
-
throw new CCIPSuiMessageVersionInvalidError()
|
|
603
|
-
}
|
|
604
715
|
// default offchain token data
|
|
605
716
|
return Promise.resolve(request.message.tokenAmounts.map(() => undefined))
|
|
606
717
|
}
|
|
@@ -618,19 +729,17 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
618
729
|
receiverObjectIds?: string[]
|
|
619
730
|
},
|
|
620
731
|
): Promise<CCIPExecution> {
|
|
621
|
-
const { execReport } = opts
|
|
622
|
-
if (!this.contractsDir.offRamp || !this.contractsDir.ccip) {
|
|
623
|
-
throw new CCIPContractNotRouterError(
|
|
624
|
-
'OffRamp or CCIP address not set in contracts directory',
|
|
625
|
-
'Sui',
|
|
626
|
-
)
|
|
627
|
-
}
|
|
732
|
+
const { execReport, offRamp } = opts
|
|
628
733
|
const wallet = opts.wallet as Keypair
|
|
629
|
-
|
|
630
|
-
|
|
734
|
+
|
|
735
|
+
// Discover the CCIP package from the offramp
|
|
736
|
+
const ccip = await getCcipStateAddress(offRamp, this.client)
|
|
737
|
+
|
|
738
|
+
const ccipObjectRef = await getObjectRef(ccip, this.client)
|
|
739
|
+
const offrampStateObject = await getObjectRef(offRamp, this.client)
|
|
631
740
|
const receiverConfig = await getReceiverModule(
|
|
632
741
|
this.client,
|
|
633
|
-
|
|
742
|
+
ccip,
|
|
634
743
|
ccipObjectRef,
|
|
635
744
|
execReport.message.receiver,
|
|
636
745
|
)
|
|
@@ -638,7 +747,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
638
747
|
if (execReport.message.tokenAmounts.length !== 0) {
|
|
639
748
|
tokenConfigs = await fetchTokenConfigs(
|
|
640
749
|
this.client,
|
|
641
|
-
|
|
750
|
+
ccip,
|
|
642
751
|
ccipObjectRef,
|
|
643
752
|
execReport.message.tokenAmounts as CCIPMessage<typeof CCIPVersion.V1_6>['tokenAmounts'],
|
|
644
753
|
)
|
|
@@ -646,8 +755,8 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
646
755
|
|
|
647
756
|
const input: SuiManuallyExecuteInput = {
|
|
648
757
|
executionReport: execReport as ExecutionReport<CCIPMessage_V1_6_Sui>,
|
|
649
|
-
offrampAddress:
|
|
650
|
-
ccipAddress:
|
|
758
|
+
offrampAddress: offRamp,
|
|
759
|
+
ccipAddress: ccip,
|
|
651
760
|
ccipObjectRef,
|
|
652
761
|
offrampStateObject,
|
|
653
762
|
receiverConfig,
|
|
@@ -743,4 +852,52 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
743
852
|
async getFeeTokens(_router: string): Promise<never> {
|
|
744
853
|
return Promise.reject(new CCIPNotImplementedError('SuiChain.getFeeTokens'))
|
|
745
854
|
}
|
|
855
|
+
|
|
856
|
+
/** {@inheritDoc ChainStatic.buildMessageForDest} */
|
|
857
|
+
static override buildMessageForDest(
|
|
858
|
+
message: Parameters<ChainStatic['buildMessageForDest']>[0],
|
|
859
|
+
): AnyMessage & { extraArgs: SuiExtraArgsV1 } {
|
|
860
|
+
const gasLimit =
|
|
861
|
+
message.extraArgs && 'gasLimit' in message.extraArgs && message.extraArgs.gasLimit != null
|
|
862
|
+
? message.extraArgs.gasLimit
|
|
863
|
+
: message.data && dataLength(message.data)
|
|
864
|
+
? DEFAULT_GAS_LIMIT
|
|
865
|
+
: 0n
|
|
866
|
+
const allowOutOfOrderExecution =
|
|
867
|
+
message.extraArgs &&
|
|
868
|
+
'allowOutOfOrderExecution' in message.extraArgs &&
|
|
869
|
+
message.extraArgs.allowOutOfOrderExecution != null
|
|
870
|
+
? message.extraArgs.allowOutOfOrderExecution
|
|
871
|
+
: true
|
|
872
|
+
const tokenReceiver =
|
|
873
|
+
message.extraArgs &&
|
|
874
|
+
'tokenReceiver' in message.extraArgs &&
|
|
875
|
+
message.extraArgs.tokenReceiver != null
|
|
876
|
+
? message.extraArgs.tokenReceiver
|
|
877
|
+
: message.tokenAmounts?.length
|
|
878
|
+
? this.getAddress(message.receiver)
|
|
879
|
+
: '0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
880
|
+
const receiverObjectIds =
|
|
881
|
+
message.extraArgs &&
|
|
882
|
+
'receiverObjectIds' in message.extraArgs &&
|
|
883
|
+
message.extraArgs.receiverObjectIds?.length
|
|
884
|
+
? message.extraArgs.receiverObjectIds
|
|
885
|
+
: message.extraArgs && 'accounts' in message.extraArgs && message.extraArgs.accounts?.length
|
|
886
|
+
? message.extraArgs.accounts // populates receiverObjectIds from accounts
|
|
887
|
+
: []
|
|
888
|
+
const extraArgs: SuiExtraArgsV1 = {
|
|
889
|
+
gasLimit,
|
|
890
|
+
allowOutOfOrderExecution,
|
|
891
|
+
tokenReceiver,
|
|
892
|
+
receiverObjectIds,
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
...message,
|
|
896
|
+
extraArgs,
|
|
897
|
+
// if tokenReceiver, then message.receiver can (must?) be default
|
|
898
|
+
...(!!message.tokenAmounts?.length && {
|
|
899
|
+
receiver: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
900
|
+
}),
|
|
901
|
+
}
|
|
902
|
+
}
|
|
746
903
|
}
|