@layerzerolabs/lz-sui-sdk-v2
Version:
459 lines (420 loc) • 15.1 kB
text/typescript
import { bcs } from '@mysten/sui/bcs'
import { SuiClient } from '@mysten/sui/client'
import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { ModuleManager } from '../../module-manager'
import { MessagingFee, ObjectOptions } from '../../types'
import {
IPTBValidator,
asAddress,
asBool,
asBytes,
asBytes32,
asU32,
asU64,
asU8,
executeSimulate,
isTransactionArgument,
} from '../../utils'
const MODULE_NAME = 'counter'
export const CounterErrorCode = {
// Counter related errors (matching counter.move)
Counter_EInvalidMsgType: 0,
Counter_EInvalidValue: 1,
Counter_EInvalidNonce: 2,
Counter_EOnlyEndpoint: 3,
Counter_ESelfComposeOnly: 4,
Counter_EInvalidOApp: 5,
// OptionsBuilder related errors (matching options_builder.move)
OptionsBuilder_EInvalidSize: 1,
} as const
export class Counter {
public packageId: string
public readonly client: SuiClient
private readonly objects: ObjectOptions
constructor(
packageId: string,
client: SuiClient,
objects: ObjectOptions,
private readonly moduleManager: ModuleManager
) {
this.packageId = packageId
this.client = client
this.objects = objects
}
// === Set Functions ===
/**
* Initialize counter with LayerZero receive and compose information
* @param tx - The transaction to add the move call to
* @param lzReceiveInfo - LayerZero receive information transaction argument
* @param lzComposeInfo - LayerZero compose information transaction argument
*/
initCounterMoveCall(tx: Transaction, lzReceiveInfo: TransactionArgument, lzComposeInfo: TransactionArgument): void {
tx.moveCall({
target: this.#target('init_counter'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
tx.object(this.objects.counterAdminCap),
tx.object(this.objects.endpointV2),
lzReceiveInfo,
lzComposeInfo,
],
})
}
/**
* Quote messaging fees for sending counter increment
* @param dstEid - Destination endpoint ID
* @param msgType - Message type (SEND or SEND_AND_CALL)
* @param options - Execution options as bytes
* @param payInZero - Whether to pay in ZRO tokens
* @returns Promise<MessagingFee> - The calculated messaging fees
*/
async quote(
dstEid: EndpointId | TransactionArgument,
msgType: number | TransactionArgument,
options: Uint8Array | TransactionArgument,
payInZero: boolean | TransactionArgument,
validators?: IPTBValidator[]
): Promise<MessagingFee> {
const tx = new Transaction()
const quoteCall = tx.moveCall({
target: this.#target('quote'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
asU32(tx, dstEid),
asU8(tx, msgType),
asBytes(tx, options),
asBool(tx, payInZero),
],
})
return this.moduleManager.getEndpoint().quote(tx, quoteCall, undefined, validators)
}
/**
* Increment counter on destination chain
* @param tx - The transaction to add the move call to
* @param sender - Sender address for ZRO token operations
* @param dstEid - Destination endpoint ID
* @param msgType - Message type (SEND or SEND_AND_CALL)
* @param options - Execution options as bytes
* @param nativeFee - Native token fee amount
* @param zroFee - ZRO token fee amount
* @param refundAddress - Address for fee refunds
*/
async incrementMoveCall(
tx: Transaction,
sender: string,
dstEid: EndpointId | TransactionArgument,
msgType: number | TransactionArgument,
options: Uint8Array | TransactionArgument,
nativeFee: bigint | TransactionArgument,
zroFee: bigint | TransactionArgument,
refundAddress: string | TransactionArgument,
validators?: IPTBValidator[]
): Promise<void> {
const [nativeToken] = tx.splitCoins(tx.gas, [asU64(tx, nativeFee)])
const zroToken = isTransactionArgument(zroFee)
? zroFee
: await this.moduleManager.getZro().splitOptionZroTokenMoveCall(tx, sender, zroFee)
const incrementCall = tx.moveCall({
target: this.#target('increment'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
asU32(tx, dstEid),
asU8(tx, msgType),
asBytes(tx, options),
nativeToken,
zroToken,
asAddress(tx, refundAddress),
],
})
await this.moduleManager.getEndpoint().populateSendTransaction(tx, incrementCall, sender, validators)
}
/**
* Set composer information for counter
* @param tx - The transaction to add the move call to
* @param composerInfo - Composer information including lz_compose execution information as bytes
*/
setComposerInfoMoveCall(tx: Transaction, composerInfo: Uint8Array | TransactionArgument): void {
tx.moveCall({
target: this.#target('set_composer_info'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
tx.object(this.objects.counterAdminCap),
tx.object(this.objects.endpointV2),
asBytes(tx, composerInfo),
],
})
}
// === View Functions ===
/**
* Get counter EID
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the counter EID
*/
getEidMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('eid'),
arguments: [tx.object(this.objects.counter)],
})
}
/**
* Get counter EID
* @returns Promise<number> - The counter EID
*/
async getEid(): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getEidMoveCall(tx)
},
(result) => bcs.U32.parse(result[0].value)
)
}
/**
* Get call capability address for counter
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the call capability address
*/
getCallCapAddressMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('call_cap_address'),
arguments: [tx.object(this.objects.counter)],
})
}
/**
* Get call capability address for counter
* @returns Promise<string> - The call capability address
*/
async getCallCapAddress(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.getCallCapAddressMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get composer address for counter
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the composer address
*/
getComposerAddressMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('composer_address'),
arguments: [tx.object(this.objects.counter)],
})
}
/**
* Get composer address for counter
* @returns Promise<string> - The composer address
*/
async getComposerAddress(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.getComposerAddressMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get current counter value
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the counter value
*/
getCountMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('get_count'),
arguments: [tx.object(this.objects.counter)],
})
}
/**
* Get current counter value
* @returns Promise<number> - The current counter value
*/
async getCount(): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getCountMoveCall(tx)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
/**
* Get composed counter value
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the composed counter value
*/
getComposedCountMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('get_composed_count'),
arguments: [tx.object(this.objects.counter)],
})
}
/**
* Get composed counter value
* @returns Promise<number> - The composed counter value
*/
async getComposedCount(): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getComposedCountMoveCall(tx)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
/**
* Get outbound counter value for a destination
* @param tx - The transaction to add the move call to
* @param dstEid - Destination endpoint ID
* @returns Transaction result containing the outbound counter value
*/
getOutboundCountMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('get_outbound_count'),
arguments: [tx.object(this.objects.counter), asU32(tx, dstEid)],
})
}
/**
* Get outbound counter value for a destination
* @param dstEid - Destination endpoint ID
* @returns Promise<number> - The outbound counter value
*/
async getOutboundCount(dstEid: number): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getOutboundCountMoveCall(tx, dstEid)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
/**
* Get inbound counter value from a source
* @param tx - The transaction to add the move call to
* @param srcEid - Source endpoint ID
* @returns Transaction result containing the inbound counter value
*/
getInboundCountMoveCall(tx: Transaction, srcEid: number | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('get_inbound_count'),
arguments: [tx.object(this.objects.counter), asU32(tx, srcEid)],
})
}
/**
* Get inbound counter value from a source
* @param srcEid - Source endpoint ID
* @returns Promise<number> - The inbound counter value
*/
async getInboundCount(srcEid: number): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getInboundCountMoveCall(tx, srcEid)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
nextNonceMoveCall(
tx: Transaction,
srcEid: number | TransactionArgument,
sender: Uint8Array | TransactionArgument
): TransactionResult {
return tx.moveCall({
target: this.#target('next_nonce'),
arguments: [
tx.object(this.objects.counter),
asU32(tx, srcEid),
asBytes32(tx, sender, this.moduleManager.getUtils()),
],
})
}
async nextNonce(srcEid: number, sender: Uint8Array): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.nextNonceMoveCall(tx, srcEid, sender)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
getMaxReceivedNonceMoveCall(
tx: Transaction,
srcEid: number | TransactionArgument,
sender: Uint8Array | TransactionArgument
): TransactionResult {
return tx.moveCall({
target: this.#target('get_max_received_nonce'),
arguments: [
tx.object(this.objects.counter),
asU32(tx, srcEid),
asBytes32(tx, sender, this.moduleManager.getUtils()),
],
})
}
async getMaxReceivedNonce(srcEid: number, sender: Uint8Array): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.getMaxReceivedNonceMoveCall(tx, srcEid, sender)
},
(result) => Number(bcs.U64.parse(result[0].value))
)
}
isOrderedNonceMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('is_ordered_nonce'),
arguments: [tx.object(this.objects.counter)],
})
}
async isOrderedNonce(): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isOrderedNonceMoveCall(tx)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
// === PTB Builder Functions ===
lzReceiveInfoMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('lz_receive_info', 'counter_ptb_builder'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
tx.object(this.objects.endpointV2),
tx.object(this.objects.endpointPtbBuilder),
],
})
}
lzComposeInfoMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('lz_compose_info', 'counter_ptb_builder'),
arguments: [
tx.object(this.objects.counter),
tx.object(this.objects.counterOapp),
tx.object(this.objects.endpointV2),
tx.object(this.objects.endpointPtbBuilder),
],
})
}
// === Private Functions ===
/**
* Generate the full target path for move calls
* @param name - The function name to call
* @param module_name - The module name (defaults to 'counter')
* @returns The full module path for the move call
* @private
*/
#target(name: string, module_name = MODULE_NAME): string {
return `${this.packageId}::${module_name}::${name}`
}
}