@layerzerolabs/lz-sui-oft-sdk-v2 3.0.127-sui.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.
@@ -0,0 +1,978 @@
1
+ import { bcs } from '@mysten/sui/bcs'
2
+ import { SuiClient } from '@mysten/sui/client'
3
+ import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions'
4
+
5
+ import {
6
+ SDK,
7
+ asAddress,
8
+ asArgWithTx,
9
+ asArgWithTxAsync,
10
+ asBool,
11
+ asBytes,
12
+ asBytes32,
13
+ asObject,
14
+ asU16,
15
+ asU32,
16
+ asU64,
17
+ executeSimulate,
18
+ isTransactionArgument,
19
+ } from '@layerzerolabs/lz-sui-sdk-v2'
20
+ import type { MessagingFee, ObjectOptions } from '@layerzerolabs/lz-sui-sdk-v2'
21
+
22
+ import { parseOFTFeeDetails, parseOFTLimit, parseOFTReceipt } from '../bcs/oft'
23
+ import { OFTFeeDetail, OFTLimit, OFTReceipt, SendParam } from '../types'
24
+
25
+ const MODULE_NAME = 'oft'
26
+
27
+ // ==========================================
28
+ // ERROR CODES
29
+ // ==========================================
30
+ // Standard error codes that may be returned by OFT operations
31
+
32
+ export const OFTErrorCode = {
33
+ // OFT related errors
34
+ OFT_EComposeMsgNotAllowed: 1,
35
+ OFT_EComposeMsgRequired: 2,
36
+ OFT_EInvalidComposeQueue: 3,
37
+ OFT_EInvalidLocalDecimals: 4,
38
+ OFT_EPaused: 5,
39
+ OFT_EPauseUnchanged: 6,
40
+ OFT_ESlippageExceeded: 7,
41
+ OFT_EInsufficientBalance: 8,
42
+ } as const
43
+
44
+ /**
45
+ * OFT (Omnichain Fungible Token) Class
46
+ *
47
+ * The OFT class provides a comprehensive interface for interacting with LayerZero's
48
+ * Omnichain Fungible Token standard on the Sui blockchain. OFTs enable seamless
49
+ * cross-chain token transfers while maintaining fungibility across multiple networks.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // Initialize OFT instance
54
+ * const oft = new OFT(protocolSDK, corePackageId, packageId, oftObjectId, coinType, adminCapId);
55
+ *
56
+ * // Send tokens cross-chain
57
+ * const tx = new Transaction();
58
+ * const coin = await oft.splitCoinMoveCall(tx, sender, amount);
59
+ * await oft.sendMoveCall(tx, sender, coin, sendParam, nativeFee, zroFee, refundAddress, messagingChannel);
60
+ * tx.transferObjects([coin], sender);
61
+ * ...
62
+ *
63
+ * // Quote transfer fees
64
+ * const fees = await oft.quoteSend(sender, sendParam, payInZro);
65
+ * ```
66
+ */
67
+ export class OFT {
68
+ /** Sui client for blockchain interactions */
69
+ public readonly client: SuiClient
70
+ /** The package ID of the OFT implementation (specific token contract) */
71
+ public packageId: string
72
+ /** The package ID of the core OFT framework (shared OFT functionality) */
73
+ public corePackageId: string
74
+ /** LayerZero protocol object references (endpoint, messaging channels, etc.) */
75
+ private readonly objects: ObjectOptions
76
+ /** The unique object ID of this OFT instance on Sui */
77
+ private readonly oftObjectId: string
78
+ /** Admin capability object ID for privileged operations (optional for read-only usage) */
79
+ private readonly adminCapId?: string
80
+ /** The Sui coin type this OFT represents (e.g., "0x123::mycoin::MYCOIN") */
81
+ private readonly coinType: string
82
+ /** Reference to the LayerZero protocol SDK for cross-chain operations */
83
+ private readonly protocolSDK: SDK
84
+
85
+ /**
86
+ * Creates a new OFT instance for interacting with an Omnichain Fungible Token
87
+ *
88
+ * @param protocolSDK - The LayerZero protocol SDK instance providing core cross-chain functionality
89
+ * @param corePackageId - The package ID of the core OFT framework (shared across all OFT implementations)
90
+ * @param packageId - The package ID of the specific OFT token implementation
91
+ * @param oftObjectId - The unique object ID of this OFT instance
92
+ * @param coinType - The Sui coin type string (e.g., "0x123::mycoin::MYCOIN")
93
+ * @param adminCapId - Optional admin capability object ID for privileged operations (required for admin functions)
94
+ */
95
+ constructor(
96
+ protocolSDK: SDK,
97
+ corePackageId: string, // the OFT package id
98
+ packageId: string, // the Implementation's package id
99
+ oftObjectId: string,
100
+ coinType: string,
101
+ adminCapId?: string
102
+ ) {
103
+ this.protocolSDK = protocolSDK
104
+ this.packageId = packageId
105
+ this.corePackageId = corePackageId
106
+ this.client = protocolSDK.client
107
+ this.objects = protocolSDK.objects
108
+ this.oftObjectId = oftObjectId
109
+ this.adminCapId = adminCapId
110
+ this.coinType = coinType
111
+ }
112
+
113
+ // ==========================================
114
+ // ADMIN FUNCTIONS
115
+ // ==========================================
116
+ // These functions require admin privileges and are used for OFT configuration
117
+ // and management. They require the adminCapId to be provided during construction.
118
+
119
+ /**
120
+ * Register OFT as an OApp with LayerZero endpoint
121
+ * @param tx - The transaction to add the move call to
122
+ * @param lzReceiveInfo - PTB Builder lzReceiveInfoMoveCall result, used for protocol SDK to dynamically build the PTB
123
+ * for OFT's lzReceive operation
124
+ */
125
+ registerOAppMoveCall(tx: Transaction, lzReceiveInfo: Uint8Array | TransactionArgument): void {
126
+ tx.moveCall({
127
+ target: this.#target('register_oapp'),
128
+ typeArguments: [this.coinType],
129
+ arguments: [
130
+ tx.object(this.oftObjectId),
131
+ tx.object(this.#adminCapId()),
132
+ tx.object(this.objects.endpointV2),
133
+ asBytes(tx, lzReceiveInfo),
134
+ ],
135
+ })
136
+ }
137
+
138
+ /**
139
+ * Set enforced options for OFT messaging to a destination
140
+ * @param tx - The transaction to add the move call to
141
+ * @param eid - Endpoint ID
142
+ * @param msgType - Message type (SEND or SEND_AND_CALL)
143
+ * @param options - Enforced options as bytes
144
+ */
145
+ setEnforcedOptionsMoveCall(
146
+ tx: Transaction,
147
+ eid: number | TransactionArgument,
148
+ msgType: number | TransactionArgument,
149
+ options: Uint8Array | TransactionArgument
150
+ ): void {
151
+ tx.moveCall({
152
+ target: this.#target('set_enforced_options'),
153
+ typeArguments: [this.coinType],
154
+ arguments: [
155
+ tx.object(this.oftObjectId),
156
+ tx.object(this.#adminCapId()),
157
+ asU32(tx, eid),
158
+ asU16(tx, msgType),
159
+ asBytes(tx, options),
160
+ ],
161
+ })
162
+ }
163
+
164
+ /**
165
+ * Set peer OFT on another chain
166
+ * @param tx - The transaction to add the move call to
167
+ * @param messagingChannel - The messaging channel object ID
168
+ * @param eid - Peer endpoint ID
169
+ * @param peer - Peer OFT address as bytes
170
+ */
171
+ setPeerMoveCall(
172
+ tx: Transaction,
173
+ messagingChannel: string | TransactionArgument,
174
+ eid: number | TransactionArgument,
175
+ peer: Uint8Array | TransactionArgument
176
+ ): void {
177
+ tx.moveCall({
178
+ target: this.#target('set_peer'),
179
+ typeArguments: [this.coinType],
180
+ arguments: [
181
+ tx.object(this.oftObjectId),
182
+ tx.object(this.#adminCapId()),
183
+ tx.object(this.objects.endpointV2),
184
+ asObject(tx, messagingChannel),
185
+ asU32(tx, eid),
186
+ asBytes32(tx, peer, this.protocolSDK.getUtils()),
187
+ ],
188
+ })
189
+ }
190
+
191
+ /**
192
+ * Set delegate for OFT
193
+ * @param tx - The transaction to add the move call to
194
+ * @param newDelegate - The new delegate address
195
+ */
196
+ setDelegateMoveCall(tx: Transaction, newDelegate: string | TransactionArgument): void {
197
+ tx.moveCall({
198
+ target: this.#target('set_delegate'),
199
+ typeArguments: [this.coinType],
200
+ arguments: [
201
+ tx.object(this.oftObjectId),
202
+ tx.object(this.#adminCapId()),
203
+ tx.object(this.objects.endpointV2),
204
+ asAddress(tx, newDelegate),
205
+ ],
206
+ })
207
+ }
208
+
209
+ /**
210
+ * Set pause state for OFT operations
211
+ * @param tx - The transaction to add the move call to
212
+ * @param pause - Whether to pause or unpause operations
213
+ */
214
+ setPauseMoveCall(tx: Transaction, pause: boolean | TransactionArgument): void {
215
+ tx.moveCall({
216
+ target: this.#target('set_pause'),
217
+ typeArguments: [this.coinType],
218
+ arguments: [tx.object(this.oftObjectId), tx.object(this.#adminCapId()), asBool(tx, pause)],
219
+ })
220
+ }
221
+
222
+ // ==========================================
223
+ // FEE MANAGEMENT ADMIN FUNCTIONS
224
+ // ==========================================
225
+ // Configure fee collection and distribution for OFT transfers
226
+
227
+ /**
228
+ * Set fee deposit address for OFT fees
229
+ * @param tx - The transaction to add the move call to
230
+ * @param feeDepositAddress - The new fee deposit address
231
+ */
232
+ setFeeDepositAddressMoveCall(tx: Transaction, feeDepositAddress: string | TransactionArgument): void {
233
+ tx.moveCall({
234
+ target: this.#target('set_fee_deposit_address'),
235
+ typeArguments: [this.coinType],
236
+ arguments: [tx.object(this.oftObjectId), tx.object(this.#adminCapId()), asAddress(tx, feeDepositAddress)],
237
+ })
238
+ }
239
+
240
+ /**
241
+ * Set fee basis points for OFT transfers
242
+ * @param tx - The transaction to add the move call to
243
+ * @param feeBps - The fee in basis points
244
+ */
245
+ setFeeBpsMoveCall(tx: Transaction, feeBps: bigint | number | string | TransactionArgument): void {
246
+ tx.moveCall({
247
+ target: this.#target('set_fee_bps'),
248
+ typeArguments: [this.coinType],
249
+ arguments: [tx.object(this.oftObjectId), tx.object(this.#adminCapId()), asU64(tx, feeBps)],
250
+ })
251
+ }
252
+
253
+ // ==========================================
254
+ // RATE LIMITER ADMIN FUNCTIONS
255
+ // ==========================================
256
+ // Configure transfer rate limits for security and compliance
257
+
258
+ /**
259
+ * Set rate limit for OFT transfers
260
+ * @param tx - The transaction to add the move call to
261
+ * @param eid - Endpoint ID
262
+ * @param inbound - Whether this is for inbound or outbound transfers
263
+ * @param rateLimit - Rate limit amount
264
+ * @param windowSeconds - Time window in seconds
265
+ */
266
+ setRateLimitMoveCall(
267
+ tx: Transaction,
268
+ eid: number | TransactionArgument,
269
+ inbound: boolean | TransactionArgument,
270
+ rateLimit: bigint | number | string | TransactionArgument,
271
+ windowSeconds: bigint | number | string | TransactionArgument
272
+ ): void {
273
+ tx.moveCall({
274
+ target: this.#target('set_rate_limit'),
275
+ typeArguments: [this.coinType],
276
+ arguments: [
277
+ tx.object(this.oftObjectId),
278
+ tx.object(this.#adminCapId()),
279
+ asU32(tx, eid),
280
+ asBool(tx, inbound),
281
+ asU64(tx, rateLimit),
282
+ asU64(tx, windowSeconds),
283
+ tx.object.clock(),
284
+ ],
285
+ })
286
+ }
287
+
288
+ /**
289
+ * Remove rate limit for OFT transfers
290
+ * @param tx - The transaction to add the move call to
291
+ * @param eid - Endpoint ID
292
+ * @param inbound - Whether this is for inbound or outbound transfers
293
+ */
294
+ unsetRateLimitMoveCall(
295
+ tx: Transaction,
296
+ eid: number | TransactionArgument,
297
+ inbound: boolean | TransactionArgument
298
+ ): void {
299
+ tx.moveCall({
300
+ target: this.#target('unset_rate_limit'),
301
+ typeArguments: [this.coinType],
302
+ arguments: [
303
+ tx.object(this.oftObjectId),
304
+ tx.object(this.#adminCapId()),
305
+ asU32(tx, eid),
306
+ asBool(tx, inbound),
307
+ ],
308
+ })
309
+ }
310
+
311
+ // ==========================================
312
+ // CORE FUNCTIONS
313
+ // ==========================================
314
+ // Primary OFT operations for token transfers and coin management
315
+
316
+ /**
317
+ * Splits specified amount of coins from user's wallet
318
+ * @param tx - The transaction to add the move call to
319
+ * @param owner - Address of the user whose coins to split
320
+ * @param amount - Amount of coins to split (in smallest units)
321
+ * @param limit - Maximum total number of coins to collect across all pages (default: 200)
322
+ * @param pageSize - Maximum number of coins to fetch per page (default: 50)
323
+ * @returns Promise resolving to split coin as TransactionResult
324
+ * @throws Error if insufficient coins balance or no coins found
325
+ */
326
+ async splitCoinMoveCall(
327
+ tx: Transaction,
328
+ owner: string,
329
+ amount: bigint,
330
+ limit = 200,
331
+ pageSize = 50
332
+ ): Promise<TransactionResult> {
333
+ return this.protocolSDK.getUtils().splitCoinMoveCall(tx, this.coinType, owner, amount, limit, pageSize)
334
+ }
335
+
336
+ /**
337
+ * Send OFT tokens to destination chain
338
+ * @param tx - The transaction to add the move call to
339
+ * @param sender - Sender address for ZRO token operations
340
+ * @param coinProvided - Coin object ID or transaction result to send
341
+ * @param messagingChannel - The messaging channel object ID
342
+ * @param sendParam - Send parameters including destination and amounts
343
+ * @param nativeFee - Native token fee amount
344
+ * @param zroFee - ZRO token fee amount
345
+ * @param refundAddress - Address for fee refunds
346
+ * @returns Promise<TransactionResult> - Transaction result containing send operation and receipt objects
347
+ */
348
+ async sendMoveCall(
349
+ tx: Transaction,
350
+ sender: string,
351
+ coinProvided: string | TransactionArgument,
352
+ sendParam: SendParam | TransactionArgument,
353
+ nativeFee: bigint | TransactionArgument,
354
+ zroFee: bigint | TransactionArgument,
355
+ refundAddress: string | TransactionArgument,
356
+ messagingChannel?: string | undefined
357
+ ): Promise<void> {
358
+ const sendParamArg = isTransactionArgument(sendParam) ? sendParam : this.#buildSendParam(tx, sendParam)
359
+ const messagingChannelArg =
360
+ messagingChannel ?? (await this.protocolSDK.getEndpoint().getMessagingChannel(this.packageId))
361
+
362
+ // The oft::send returns a tuple (Call<EndpointSendParam, MessagingReceipt>, OFTReceipt)
363
+ const transactionResult = tx.moveCall({
364
+ target: this.#target('send'),
365
+ typeArguments: [this.coinType],
366
+ arguments: [
367
+ tx.object(this.oftObjectId),
368
+ asObject(tx, coinProvided),
369
+ tx.object(this.objects.endpointV2),
370
+ tx.object(messagingChannelArg),
371
+ sendParamArg,
372
+ asArgWithTx(tx, nativeFee, (tx, val) => tx.splitCoins(tx.gas, [asU64(tx, val)])[0]),
373
+ await asArgWithTxAsync(tx, zroFee, async (tx, val) =>
374
+ this.protocolSDK.getZro().splitOptionZroTokenMoveCall(tx, sender, val)
375
+ ),
376
+ asAddress(tx, refundAddress),
377
+ tx.object.clock(),
378
+ ],
379
+ })
380
+ const endpointCall = transactionResult[0]
381
+
382
+ await this.protocolSDK
383
+ .getEndpoint()
384
+ .populateSendTransaction(tx, endpointCall as unknown as TransactionResult, sender)
385
+
386
+ tx.moveCall({
387
+ target: this.#target('confirm_send'),
388
+ typeArguments: [this.coinType],
389
+ arguments: [tx.object(this.oftObjectId), endpointCall],
390
+ })
391
+ }
392
+
393
+ // ==========================================
394
+ // QUOTE FUNCTIONS
395
+ // ==========================================
396
+ // Calculate fees, limits, and receipts before executing operations
397
+
398
+ /**
399
+ * Quote OFT sending operation with detailed fee breakdown
400
+ * @param sendParam - Send parameters for the OFT operation
401
+ * @returns Promise with limit, fee details, and receipt information
402
+ */
403
+ async quoteOft(
404
+ sendParam: SendParam | TransactionArgument
405
+ ): Promise<{ limit: OFTLimit; feeDetails: OFTFeeDetail[]; receipt: OFTReceipt }> {
406
+ return executeSimulate(
407
+ this.client,
408
+ (tx) => {
409
+ const sendParamArg = isTransactionArgument(sendParam) ? sendParam : this.#buildSendParam(tx, sendParam)
410
+ tx.moveCall({
411
+ target: this.#target('quote_oft'),
412
+ typeArguments: [this.coinType],
413
+ arguments: [tx.object(this.oftObjectId), sendParamArg, tx.object.clock()],
414
+ })
415
+ },
416
+ (result) => {
417
+ return {
418
+ limit: parseOFTLimit(result[0].value),
419
+ feeDetails: parseOFTFeeDetails(result[1].value),
420
+ receipt: parseOFTReceipt(result[2].value),
421
+ }
422
+ }
423
+ )
424
+ }
425
+
426
+ /**
427
+ * Quote messaging fees for OFT sending
428
+ * @param sender - Sender address
429
+ * @param sendParam - Send parameters for the OFT operation
430
+ * @param payInZro - Whether to pay in ZRO tokens
431
+ * @returns Promise<MessagingFee> - The calculated messaging fees
432
+ */
433
+ async quoteSend(
434
+ sender: string | TransactionArgument,
435
+ sendParam: SendParam | TransactionArgument,
436
+ payInZro: boolean | TransactionArgument
437
+ ): Promise<MessagingFee> {
438
+ const tx = new Transaction()
439
+ const sendParamArg = isTransactionArgument(sendParam) ? sendParam : this.#buildSendParam(tx, sendParam)
440
+
441
+ const quoteCall = tx.moveCall({
442
+ target: this.#target('quote_send'),
443
+ typeArguments: [this.coinType],
444
+ arguments: [tx.object(this.oftObjectId), asAddress(tx, sender), sendParamArg, asBool(tx, payInZro)],
445
+ })
446
+
447
+ return this.protocolSDK.getEndpoint().quote(tx, quoteCall)
448
+ }
449
+
450
+ // ==========================================
451
+ // VIEW FUNCTIONS
452
+ // ==========================================
453
+ // Read-only functions to query OFT state and configuration
454
+
455
+ /**
456
+ * Get OFT version information
457
+ * @param tx - The transaction to add the move call to
458
+ * @returns Transaction result containing version information
459
+ */
460
+ oftVersionMoveCall(tx: Transaction): TransactionResult {
461
+ return tx.moveCall({
462
+ target: this.#target('oft_version'),
463
+ typeArguments: [this.coinType],
464
+ arguments: [tx.object(this.oftObjectId)],
465
+ })
466
+ }
467
+
468
+ /**
469
+ * Get OFT version information
470
+ * @returns Promise<{ major: number; minor: number }> - The OFT version
471
+ */
472
+ async oftVersion(): Promise<{ major: number; minor: number }> {
473
+ return executeSimulate(
474
+ this.client,
475
+ (tx) => {
476
+ this.oftVersionMoveCall(tx)
477
+ },
478
+ (result) => {
479
+ const major = Number(bcs.U64.parse(result[0].value))
480
+ const minor = Number(bcs.U64.parse(result[1].value))
481
+ return { major, minor }
482
+ }
483
+ )
484
+ }
485
+
486
+ /**
487
+ * Get coin metadata address
488
+ * @param tx - The transaction to add the move call to
489
+ * @returns Transaction result containing coin metadata address
490
+ */
491
+ coinMetadataMoveCall(tx: Transaction): TransactionResult {
492
+ return tx.moveCall({
493
+ target: this.#target('coin_metadata'),
494
+ typeArguments: [this.coinType],
495
+ arguments: [tx.object(this.oftObjectId)],
496
+ })
497
+ }
498
+
499
+ /**
500
+ * Get coin metadata address
501
+ * @returns Promise<string> - The coin metadata address
502
+ */
503
+ async coinMetadata(): Promise<string> {
504
+ return executeSimulate(
505
+ this.client,
506
+ (tx) => {
507
+ this.coinMetadataMoveCall(tx)
508
+ },
509
+ (result) => bcs.Address.parse(result[0].value)
510
+ )
511
+ }
512
+
513
+ /**
514
+ * Get OFT admin address
515
+ * @param tx - The transaction to add the move call to
516
+ * @returns Transaction result containing the admin address
517
+ */
518
+ adminMoveCall(tx: Transaction): TransactionResult {
519
+ return tx.moveCall({
520
+ target: this.#target('admin'),
521
+ typeArguments: [this.coinType],
522
+ arguments: [tx.object(this.oftObjectId)],
523
+ })
524
+ }
525
+
526
+ /**
527
+ * Get OFT admin address
528
+ * @returns Promise<string> - The admin address
529
+ */
530
+ async admin(): Promise<string> {
531
+ return executeSimulate(
532
+ this.client,
533
+ (tx) => {
534
+ this.adminMoveCall(tx)
535
+ },
536
+ (result) => bcs.Address.parse(result[0].value)
537
+ )
538
+ }
539
+
540
+ /**
541
+ * Get shared decimals for cross-chain compatibility
542
+ * @param tx - The transaction to add the move call to
543
+ * @returns Transaction result containing shared decimals
544
+ */
545
+ sharedDecimalsMoveCall(tx: Transaction): TransactionResult {
546
+ return tx.moveCall({
547
+ target: this.#target('shared_decimals'),
548
+ typeArguments: [this.coinType],
549
+ arguments: [tx.object(this.oftObjectId)],
550
+ })
551
+ }
552
+
553
+ /**
554
+ * Get shared decimals for cross-chain compatibility
555
+ * @returns Promise<number> - The shared decimal precision
556
+ */
557
+ async sharedDecimals(): Promise<number> {
558
+ return executeSimulate(
559
+ this.client,
560
+ (tx) => {
561
+ this.sharedDecimalsMoveCall(tx)
562
+ },
563
+ (result) => bcs.U8.parse(result[0].value)
564
+ )
565
+ }
566
+
567
+ /**
568
+ * Check if OFT is paused
569
+ * @param tx - The transaction to add the move call to
570
+ * @returns Transaction result containing the paused status
571
+ */
572
+ isPausedMoveCall(tx: Transaction): TransactionResult {
573
+ return tx.moveCall({
574
+ target: this.#target('is_paused'),
575
+ typeArguments: [this.coinType],
576
+ arguments: [tx.object(this.oftObjectId)],
577
+ })
578
+ }
579
+
580
+ /**
581
+ * Check if OFT is paused
582
+ * @returns Promise<boolean> - True if OFT is paused
583
+ */
584
+ async isPaused(): Promise<boolean> {
585
+ return executeSimulate(
586
+ this.client,
587
+ (tx) => {
588
+ this.isPausedMoveCall(tx)
589
+ },
590
+ (result) => bcs.Bool.parse(result[0].value)
591
+ )
592
+ }
593
+
594
+ /**
595
+ * Check if this OFT is an adapter (wraps existing token)
596
+ * @param tx - The transaction to add the move call to
597
+ * @returns Transaction result containing the adapter status
598
+ */
599
+ isAdapterMoveCall(tx: Transaction): TransactionResult {
600
+ return tx.moveCall({
601
+ target: this.#target('is_adapter'),
602
+ typeArguments: [this.coinType],
603
+ arguments: [tx.object(this.oftObjectId)],
604
+ })
605
+ }
606
+
607
+ /**
608
+ * Check if this OFT is an adapter (wraps existing token)
609
+ * @returns Promise<boolean> - True if this is an OFT adapter
610
+ */
611
+ async isAdapter(): Promise<boolean> {
612
+ return executeSimulate(
613
+ this.client,
614
+ (tx) => {
615
+ this.isAdapterMoveCall(tx)
616
+ },
617
+ (result) => bcs.Bool.parse(result[0].value)
618
+ )
619
+ }
620
+
621
+ // ==========================================
622
+ // FEE VIEW FUNCTIONS
623
+ // ==========================================
624
+ // Query current fee configuration and settings
625
+
626
+ /**
627
+ * Get fee basis points for OFT transfers
628
+ * @param tx - The transaction to add the move call to
629
+ * @returns Transaction result containing the fee basis points
630
+ */
631
+ feeBpsMoveCall(tx: Transaction): TransactionResult {
632
+ return tx.moveCall({
633
+ target: this.#target('fee_bps'),
634
+ typeArguments: [this.coinType],
635
+ arguments: [tx.object(this.oftObjectId)],
636
+ })
637
+ }
638
+
639
+ /**
640
+ * Get fee basis points for OFT transfers
641
+ * @returns Promise<bigint> - The fee in basis points
642
+ */
643
+ async feeBps(): Promise<bigint> {
644
+ return executeSimulate(
645
+ this.client,
646
+ (tx) => {
647
+ this.feeBpsMoveCall(tx)
648
+ },
649
+ (result) => BigInt(bcs.U64.parse(result[0].value))
650
+ )
651
+ }
652
+
653
+ /**
654
+ * Get fee deposit address for OFT
655
+ * @param tx - The transaction to add the move call to
656
+ * @returns Transaction result containing the fee deposit address
657
+ */
658
+ feeDepositAddressMoveCall(tx: Transaction): TransactionResult {
659
+ return tx.moveCall({
660
+ target: this.#target('fee_deposit_address'),
661
+ typeArguments: [this.coinType],
662
+ arguments: [tx.object(this.oftObjectId)],
663
+ })
664
+ }
665
+
666
+ /**
667
+ * Get fee deposit address for OFT
668
+ * @returns Promise<string> - The fee deposit address
669
+ */
670
+ async feeDepositAddress(): Promise<string> {
671
+ return executeSimulate(
672
+ this.client,
673
+ (tx) => {
674
+ this.feeDepositAddressMoveCall(tx)
675
+ },
676
+ (result) => bcs.Address.parse(result[0].value)
677
+ )
678
+ }
679
+
680
+ // ==========================================
681
+ // RATE LIMITER VIEW FUNCTIONS
682
+ // ==========================================
683
+ // Query current rate limiting configuration and status
684
+
685
+ /**
686
+ * Get rate limit configuration for an endpoint
687
+ * @param tx - The transaction to add the move call to
688
+ * @param eid - Endpoint ID
689
+ * @param inbound - Whether this is for inbound or outbound transfers
690
+ * @returns Transaction result containing rate limit configuration
691
+ */
692
+ rateLimitConfigMoveCall(
693
+ tx: Transaction,
694
+ eid: number | TransactionArgument,
695
+ inbound: boolean | TransactionArgument
696
+ ): TransactionResult {
697
+ return tx.moveCall({
698
+ target: this.#target('rate_limit_config'),
699
+ typeArguments: [this.coinType],
700
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid), asBool(tx, inbound)],
701
+ })
702
+ }
703
+
704
+ /**
705
+ * Get rate limit configuration for an endpoint
706
+ * @param eid - Endpoint ID
707
+ * @param inbound - Whether this is for inbound or outbound transfers
708
+ * @returns Promise<{ limit: bigint; windowSeconds: bigint }> - Rate limit configuration
709
+ */
710
+ async rateLimitConfig(eid: number, inbound: boolean): Promise<{ limit: bigint; windowSeconds: bigint }> {
711
+ return executeSimulate(
712
+ this.client,
713
+ (tx) => {
714
+ this.rateLimitConfigMoveCall(tx, eid, inbound)
715
+ },
716
+ (result) => {
717
+ const limit = BigInt(bcs.U64.parse(result[0].value))
718
+ const windowSeconds = BigInt(bcs.U64.parse(result[1].value))
719
+ return { limit, windowSeconds }
720
+ }
721
+ )
722
+ }
723
+
724
+ /**
725
+ * Get current in-flight amount for rate limiting
726
+ * @param tx - The transaction to add the move call to
727
+ * @param eid - Endpoint ID
728
+ * @param inbound - Whether this is for inbound or outbound transfers
729
+ * @returns Transaction result containing in-flight amount
730
+ */
731
+ rateLimitInFlightMoveCall(
732
+ tx: Transaction,
733
+ eid: number | TransactionArgument,
734
+ inbound: boolean | TransactionArgument
735
+ ): TransactionResult {
736
+ return tx.moveCall({
737
+ target: this.#target('rate_limit_in_flight'),
738
+ typeArguments: [this.coinType],
739
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid), asBool(tx, inbound), tx.object.clock()],
740
+ })
741
+ }
742
+
743
+ /**
744
+ * Get current in-flight amount for rate limiting
745
+ * @param eid - Endpoint ID
746
+ * @param inbound - Whether this is for inbound or outbound transfers
747
+ * @returns Promise<bigint> - Current in-flight amount
748
+ */
749
+ async rateLimitInFlight(eid: number, inbound: boolean): Promise<bigint> {
750
+ return executeSimulate(
751
+ this.client,
752
+ (tx) => {
753
+ this.rateLimitInFlightMoveCall(tx, eid, inbound)
754
+ },
755
+ (result) => BigInt(bcs.U64.parse(result[0].value))
756
+ )
757
+ }
758
+
759
+ /**
760
+ * Get rate limit capacity (remaining available amount)
761
+ * @param tx - The transaction to add the move call to
762
+ * @param eid - Endpoint ID
763
+ * @param inbound - Whether this is for inbound or outbound transfers
764
+ * @returns Transaction result containing rate limit capacity
765
+ */
766
+ rateLimitCapacityMoveCall(
767
+ tx: Transaction,
768
+ eid: number | TransactionArgument,
769
+ inbound: boolean | TransactionArgument
770
+ ): TransactionResult {
771
+ return tx.moveCall({
772
+ target: this.#target('rate_limit_capacity'),
773
+ typeArguments: [this.coinType],
774
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid), asBool(tx, inbound), tx.object.clock()],
775
+ })
776
+ }
777
+
778
+ /**
779
+ * Get rate limit capacity (remaining available amount)
780
+ * @param eid - Endpoint ID
781
+ * @param inbound - Whether this is for inbound or outbound transfers
782
+ * @returns Promise<bigint> - Remaining rate limit capacity
783
+ */
784
+ async rateLimitCapacity(eid: number, inbound: boolean): Promise<bigint> {
785
+ return executeSimulate(
786
+ this.client,
787
+ (tx) => {
788
+ this.rateLimitCapacityMoveCall(tx, eid, inbound)
789
+ },
790
+ (result) => BigInt(bcs.U64.parse(result[0].value))
791
+ )
792
+ }
793
+
794
+ /**
795
+ * Combine enforced options with extra options
796
+ * @param tx - The transaction to add the move call to
797
+ * @param eid - Endpoint ID
798
+ * @param msgType - Message type
799
+ * @param extraOptions - Extra options to combine with enforced options
800
+ * @returns Transaction result containing the combined options
801
+ */
802
+ combineOptionsMoveCall(
803
+ tx: Transaction,
804
+ eid: number | TransactionArgument,
805
+ msgType: number | TransactionArgument,
806
+ extraOptions: Uint8Array | TransactionArgument
807
+ ): TransactionResult {
808
+ return tx.moveCall({
809
+ target: this.#target('combine_options'),
810
+ typeArguments: [this.coinType],
811
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid), asU16(tx, msgType), asBytes(tx, extraOptions)],
812
+ })
813
+ }
814
+
815
+ /**
816
+ * Combine enforced options with extra options
817
+ * @param eid - Endpoint ID
818
+ * @param msgType - Message type
819
+ * @param extraOptions - Extra options to combine with enforced options
820
+ * @returns Promise<Uint8Array> - The combined options as bytes
821
+ */
822
+ async combineOptions(eid: number, msgType: number, extraOptions: Uint8Array): Promise<Uint8Array> {
823
+ return executeSimulate(
824
+ this.client,
825
+ (tx) => {
826
+ this.combineOptionsMoveCall(tx, eid, msgType, extraOptions)
827
+ },
828
+ (result) => new Uint8Array(bcs.vector(bcs.u8()).parse(result[0].value))
829
+ )
830
+ }
831
+
832
+ /**
833
+ * Get enforced options for OFT messaging
834
+ * @param tx - The transaction to add the move call to
835
+ * @param eid - Endpoint ID
836
+ * @param msgType - Message type
837
+ * @returns Transaction result containing the enforced options
838
+ */
839
+ getEnforcedOptionsMoveCall(
840
+ tx: Transaction,
841
+ eid: number | TransactionArgument,
842
+ msgType: number | TransactionArgument
843
+ ): TransactionResult {
844
+ return tx.moveCall({
845
+ target: this.#target('get_enforced_options'),
846
+ typeArguments: [this.coinType],
847
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid), asU16(tx, msgType)],
848
+ })
849
+ }
850
+
851
+ /**
852
+ * Get enforced options for OFT messaging
853
+ * @param eid - Endpoint ID
854
+ * @param msgType - Message type
855
+ * @returns Promise<Uint8Array> - The enforced options as bytes
856
+ */
857
+ async getEnforcedOptions(eid: number, msgType: number): Promise<Uint8Array> {
858
+ return executeSimulate(
859
+ this.client,
860
+ (tx) => {
861
+ this.getEnforcedOptionsMoveCall(tx, eid, msgType)
862
+ },
863
+ (result) => new Uint8Array(bcs.vector(bcs.u8()).parse(result[0].value))
864
+ )
865
+ }
866
+
867
+ /**
868
+ * Check if OFT has a peer on specific endpoint
869
+ * @param tx - The transaction to add the move call to
870
+ * @param eid - Endpoint ID
871
+ * @returns Transaction result containing the peer existence status
872
+ */
873
+ hasPeerMoveCall(tx: Transaction, eid: number | TransactionArgument): TransactionResult {
874
+ return tx.moveCall({
875
+ target: this.#target('has_peer'),
876
+ typeArguments: [this.coinType],
877
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid)],
878
+ })
879
+ }
880
+
881
+ /**
882
+ * Check if OFT has a peer on specific endpoint
883
+ * @param eid - Endpoint ID
884
+ * @returns Promise<boolean> - True if peer exists on the endpoint
885
+ */
886
+ async hasPeer(eid: number): Promise<boolean> {
887
+ return executeSimulate(
888
+ this.client,
889
+ (tx) => {
890
+ this.hasPeerMoveCall(tx, eid)
891
+ },
892
+ (result) => bcs.Bool.parse(result[0].value)
893
+ )
894
+ }
895
+
896
+ /**
897
+ * Get peer OFT address on specific endpoint
898
+ * @param tx - The transaction to add the move call to
899
+ * @param eid - Endpoint ID
900
+ * @returns Transaction result containing the peer address
901
+ */
902
+ getPeerMoveCall(tx: Transaction, eid: number | TransactionArgument): TransactionResult {
903
+ return tx.moveCall({
904
+ target: this.#target('get_peer'),
905
+ typeArguments: [this.coinType],
906
+ arguments: [tx.object(this.oftObjectId), asU32(tx, eid)],
907
+ })
908
+ }
909
+
910
+ /**
911
+ * Get peer OFT address on specific endpoint
912
+ * @param eid - Endpoint ID
913
+ * @returns Promise<Uint8Array> - The peer address as bytes32
914
+ */
915
+ async getPeer(eid: number): Promise<Uint8Array> {
916
+ return executeSimulate(
917
+ this.client,
918
+ (tx) => {
919
+ this.getPeerMoveCall(tx, eid)
920
+ },
921
+ (result) => {
922
+ // Assuming Bytes32 is returned as vector<u8>
923
+ return new Uint8Array(bcs.vector(bcs.u8()).parse(result[0].value))
924
+ }
925
+ )
926
+ }
927
+
928
+ // ==========================================
929
+ // PRIVATE HELPER FUNCTIONS
930
+ // ==========================================
931
+ // Internal utility functions for OFT operations
932
+
933
+ /**
934
+ * Build SendParam struct for OFT operations
935
+ * @param tx - The transaction to add the move call to
936
+ * @param param - Send parameters to build
937
+ * @returns Transaction result containing the SendParam struct
938
+ * @private
939
+ */
940
+ #buildSendParam(tx: Transaction, param: SendParam): TransactionResult {
941
+ return tx.moveCall({
942
+ target: this.#target('create', 'send_param'),
943
+ arguments: [
944
+ asU32(tx, param.dstEid),
945
+ asBytes32(tx, param.to, this.protocolSDK.getUtils()),
946
+ asU64(tx, param.amountLd),
947
+ asU64(tx, param.minAmountLd),
948
+ asBytes(tx, param.extraOptions),
949
+ asBytes(tx, param.composeMsg),
950
+ asBytes(tx, param.oftCmd),
951
+ ],
952
+ })
953
+ }
954
+
955
+ /**
956
+ * Generate the full target path for move calls
957
+ * @param name - The function name to call
958
+ * @param module_name - The module name (defaults to 'oft')
959
+ * @returns The full module path for the move call
960
+ * @private
961
+ */
962
+ #target(name: string, module_name = MODULE_NAME): string {
963
+ return `${this.corePackageId}::${module_name}::${name}`
964
+ }
965
+
966
+ /**
967
+ * Get the admin capability ID, throwing an error if not available
968
+ * @returns The admin capability ID
969
+ * @throws Error if admin capability ID was not provided during construction
970
+ * @private
971
+ */
972
+ #adminCapId(): string {
973
+ if (this.adminCapId === undefined) {
974
+ throw new Error('Admin cap ID not found')
975
+ }
976
+ return this.adminCapId
977
+ }
978
+ }