@layerzerolabs/lz-sui-sdk-v2
Version:
1,171 lines (1,090 loc) • 42.3 kB
text/typescript
import { bcs } from '@mysten/sui/bcs'
import { SuiClient } from '@mysten/sui/client'
import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions'
import { DstConfigBcs } from '../../bcs'
import { ModuleManager } from '../../module-manager'
import { DstConfig, NativeDropParams, ObjectOptions } from '../../types'
import {
asAddress,
asArgWithTx,
asBool,
asBytes,
asBytes32,
asObject,
asU128,
asU16,
asU32,
asU64,
executeSimulate,
} from '../../utils'
const MODULE_NAME = 'executor_worker'
export const ExecutorErrorCode = {
// Executor related errors (with Executor_ prefix)
Executor_EEidNotSupported: 1,
Executor_EInvalidNativeDropAmount: 2,
// ExecutorInfoV1 related errors (matching executor_info_v1.move)
ExecutorInfoV1_EInvalidData: 1,
ExecutorInfoV1_EInvalidVersion: 2,
} as const
export class Executor {
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
}
// === Witness Functions ===
/**
* Create a LayerZero witness for Executor package whitelist registration
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the LayerZero witness
*/
createLayerZeroWitnessMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: `${this.packageId}::executor_witness::new`,
arguments: [],
})
}
// Job Assignment and Fee Functions
/**
* Assign execution job for Executor (called via PTB with Call created by send function in ULN302)
* @param tx - The transaction to add the move call to
* @param call - The call transaction result from ULN302
* @returns Transaction result containing the job assignment call
*/
assignJobMoveCall(tx: Transaction, call: TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('assign_job'),
arguments: [tx.object(this.objects.executor), call],
})
}
/**
* Confirm assign job operation with fee calculation
* @param tx - The transaction to add the move call to
* @param executorCall - The executor call transaction result
* @param feelibCall - The fee library call transaction result
*/
confirmAssignJobMoveCall(
tx: Transaction,
executorCall: TransactionArgument,
feelibCall: TransactionArgument
): void {
tx.moveCall({
target: this.#target('confirm_assign_job'),
arguments: [tx.object(this.objects.executor), executorCall, feelibCall],
})
}
/**
* Get fee for execution (using Call created by quote function in ULN302)
* @param tx - The transaction to add the move call to
* @param call - The call transaction result from ULN302
* @returns Transaction result containing the fee calculation call
*/
getFeeMoveCall(tx: Transaction, call: TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('get_fee'),
arguments: [tx.object(this.objects.executor), call],
})
}
/**
* Confirm get fee operation with fee library
* @param tx - The transaction to add the move call to
* @param executorCall - The executor call transaction result
* @param feelibCall - The fee library call transaction result
*/
confirmGetFeeMoveCall(tx: Transaction, executorCall: TransactionArgument, feelibCall: TransactionArgument): void {
tx.moveCall({
target: this.#target('confirm_get_fee'),
arguments: [tx.object(this.objects.executor), executorCall, feelibCall],
})
}
// Execution Functions
/**
* Execute LayerZero receive operation (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param messagingChannel - The messaging channel object ID
* @param srcEid - Source endpoint ID
* @param sender - Sender address as bytes
* @param nonce - Message nonce
* @param guid - Globally unique identifier as bytes
* @param message - Message payload as bytes
* @param extraData - Additional execution data (optional)
* @param value - Native token value to transfer
* @returns Transaction result containing the execution call capability
*/
executeLzReceiveMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
messagingChannel: string | TransactionArgument,
srcEid: number | TransactionArgument,
sender: Uint8Array | TransactionArgument,
nonce: bigint | number | string | TransactionArgument,
guid: Uint8Array | TransactionArgument,
message: Uint8Array | TransactionArgument,
extraData: Uint8Array | TransactionArgument = new Uint8Array(),
value = 0n
): TransactionResult {
return tx.moveCall({
target: this.#target('execute_lz_receive'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
tx.object(this.objects.endpointV2),
asObject(tx, messagingChannel),
asU32(tx, srcEid),
asBytes32(tx, sender, this.moduleManager.getUtils()),
asU64(tx, nonce),
asBytes32(tx, guid, this.moduleManager.getUtils()),
asBytes(tx, message),
asBytes(tx, extraData),
asArgWithTx(tx, value, (tx, val) => this.moduleManager.getUtils().createOptionSuiMoveCall(tx, val)),
],
})
}
/**
* Execute LayerZero compose operation (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param composeQueue - The compose queue object ID
* @param from - Source address
* @param guid - Globally unique identifier as bytes
* @param index - Compose message index
* @param message - Message payload as bytes
* @param extraData - Additional execution data (optional)
* @param value - Native token value to transfer
* @returns Transaction result containing the execution call capability
*/
executeLzComposeMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
composeQueue: string | TransactionArgument,
from: string | TransactionArgument,
guid: Uint8Array | TransactionArgument,
index: number | TransactionArgument,
message: Uint8Array | TransactionArgument,
extraData: Uint8Array | TransactionArgument = new Uint8Array(),
value = 0n
): TransactionResult {
return tx.moveCall({
target: this.#target('execute_lz_compose'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
tx.object(this.objects.endpointV2),
asObject(tx, composeQueue),
asAddress(tx, from),
asBytes32(tx, guid, this.moduleManager.getUtils()),
asU16(tx, index),
asBytes(tx, message),
asBytes(tx, extraData),
asArgWithTx(tx, value, (tx, val) => this.moduleManager.getUtils().createOptionSuiMoveCall(tx, val)),
],
})
}
// Alert Functions
/**
* Record a failed lz_receive execution for off-chain processing (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param srcEid - Source endpoint ID
* @param sender - Sender address as bytes
* @param nonce - Message nonce
* @param receiver - Receiver address
* @param guid - Globally unique identifier as bytes
* @param gas - Gas limit used for the execution attempt
* @param value - Native token value included with the message
* @param message - Message payload as bytes
* @param extraData - Additional execution data
* @param reason - Error message or failure reason
*/
lzReceiveAlertMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
srcEid: number | TransactionArgument,
sender: Uint8Array | TransactionArgument,
nonce: bigint | number | string | TransactionArgument,
receiver: string | TransactionArgument,
guid: Uint8Array | TransactionArgument,
gas: bigint | number | string | TransactionArgument,
value: bigint | number | string | TransactionArgument,
message: Uint8Array | TransactionArgument,
extraData: Uint8Array | TransactionArgument,
reason: string | TransactionArgument
): void {
tx.moveCall({
target: this.#target('lz_receive_alert'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
asU32(tx, srcEid),
asBytes32(tx, sender, this.moduleManager.getUtils()),
asU64(tx, nonce),
asAddress(tx, receiver),
asBytes32(tx, guid, this.moduleManager.getUtils()),
asU64(tx, gas),
asU64(tx, value),
asBytes(tx, message),
asBytes(tx, extraData),
asArgWithTx(tx, reason, (tx, val) => tx.pure.string(val)),
],
})
}
/**
* Record a failed lz_compose execution for off-chain processing (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param from - Source address
* @param to - Destination address
* @param guid - Globally unique identifier as bytes
* @param index - Compose message index
* @param gas - Gas limit used for the execution attempt
* @param value - Native token value included with the compose
* @param message - Compose message payload as bytes
* @param extraData - Additional execution data
* @param reason - Error message or failure reason
*/
lzComposeAlertMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
from: string | TransactionArgument,
to: string | TransactionArgument,
guid: Uint8Array | TransactionArgument,
index: number | TransactionArgument,
gas: bigint | number | string | TransactionArgument,
value: bigint | number | string | TransactionArgument,
message: Uint8Array | TransactionArgument,
extraData: Uint8Array | TransactionArgument,
reason: string | TransactionArgument
): void {
tx.moveCall({
target: this.#target('lz_compose_alert'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
asAddress(tx, from),
asAddress(tx, to),
asBytes32(tx, guid, this.moduleManager.getUtils()),
asU16(tx, index),
asU64(tx, gas),
asU64(tx, value),
asBytes(tx, message),
asBytes(tx, extraData),
asArgWithTx(tx, reason, (tx, val) => tx.pure.string(val)),
],
})
}
// Native Drop Functions
/**
* Native drop function (admin only)
* Takes a Coin<SUI> from caller and distributes it to recipients according to params
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param srcEid - Source endpoint ID
* @param sender - Sender address as bytes
* @param dstEid - Destination endpoint ID
* @param oapp - OApp address
* @param nonce - Message nonce
* @param nativeDropParams - Array of native drop parameters
* @param paymentCoin - Payment coin for the drop
*/
nativeDropMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
srcEid: number | TransactionArgument,
sender: Uint8Array | TransactionArgument,
dstEid: number | TransactionArgument,
oapp: string | TransactionArgument,
nonce: bigint | number | string | TransactionArgument,
nativeDropParams: NativeDropParams[],
paymentCoin: TransactionArgument
): void {
// Create individual NativeDropParams move calls for each parameter
const dropParamCalls = nativeDropParams.map((param) =>
tx.moveCall({
target: this.#target('new_native_drop_params', 'native_drop_type'),
arguments: [asAddress(tx, param.receiver), asU64(tx, param.amount)],
})
)
tx.moveCall({
target: this.#target('native_drop'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
asU32(tx, srcEid),
asBytes32(tx, sender, this.moduleManager.getUtils()),
asU32(tx, dstEid),
asAddress(tx, oapp),
asU64(tx, nonce),
tx.makeMoveVec({
type: `${this.packageId}::native_drop_type::NativeDropParams`,
elements: dropParamCalls,
}),
paymentCoin,
],
})
}
// === Set Functions ===
/**
* Set default multiplier basis points for fee calculation (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param multiplierBps - The multiplier in basis points
*/
setDefaultMultiplierBpsMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
multiplierBps: number | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_default_multiplier_bps'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap), asU16(tx, multiplierBps)],
})
}
/**
* Set deposit address for executor fees (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param depositAddress - The new deposit address
*/
setDepositAddressMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
depositAddress: string | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_deposit_address'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap), asAddress(tx, depositAddress)],
})
}
/**
* Set destination configuration for executor (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param dstEid - Destination endpoint ID
* @param config - Destination configuration parameters
*/
setDstConfigMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
dstEid: number | TransactionArgument,
config: DstConfig
): void {
const configCall = tx.moveCall({
target: this.#target('create_dst_config', 'executor_type'),
arguments: [
asU64(tx, config.lzReceiveBaseGas),
asU64(tx, config.lzComposeBaseGas),
asU16(tx, config.multiplierBps),
asU128(tx, config.floorMarginUsd),
asU128(tx, config.nativeCap),
],
})
tx.moveCall({
target: this.#target('set_dst_config'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap), asU32(tx, dstEid), configCall],
})
}
/**
* Set price feed for executor (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param priceFeed - The price feed address
*/
setPriceFeedMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
priceFeed: string | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_price_feed'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap), asAddress(tx, priceFeed)],
})
}
/**
* Set PTB builder move calls for executor worker operations (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param targetPtbBuilder - Target PTB builder address
* @param getFeeMoveCalls - Get fee move calls transaction argument
* @param assignJobMoveCalls - Assign job move calls transaction argument
* @returns Transaction result containing the set PTB builder call
*/
setPtbBuilderMoveCallsMoveCall(
tx: Transaction,
targetPtbBuilder: string | TransactionArgument,
getFeeMoveCalls: TransactionArgument,
assignJobMoveCalls: TransactionArgument
): TransactionResult {
return tx.moveCall({
target: this.#target('set_ptb_builder_move_calls'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, this.objects.executorOwnerCap),
asAddress(tx, targetPtbBuilder),
getFeeMoveCalls, // First element of Executor PTB result tuple
assignJobMoveCalls, // Second element of Executor PTB result tuple
],
})
}
/**
* Set supported option types for a destination EID (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param dstEid - Destination endpoint ID
* @param optionTypes - Array of supported option type values
*/
setSupportedOptionTypesMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
dstEid: number | TransactionArgument,
optionTypes: number[]
): void {
tx.moveCall({
target: this.#target('set_supported_option_types'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, adminCap),
asU32(tx, dstEid),
tx.pure(bcs.vector(bcs.u8()).serialize(optionTypes)),
],
})
}
/**
* Set worker fee library for executor (admin only)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID
* @param workerFeeLib - The worker fee library address
*/
setWorkerFeeLibMoveCall(
tx: Transaction,
adminCap: string | TransactionArgument,
workerFeeLib: string | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_worker_fee_lib'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap), asAddress(tx, workerFeeLib)],
})
}
/**
* Set admin role (grant or revoke) (owner only)
* @param tx - The transaction to add the move call to
* @param ownerCap - The owner capability object ID
* @param admin - The admin address
* @param active - Whether to grant or revoke admin role
*/
setAdminMoveCall(
tx: Transaction,
ownerCap: string | TransactionArgument,
admin: string | TransactionArgument,
active: boolean | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_admin'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, ownerCap),
asAddress(tx, admin),
asBool(tx, active),
],
})
}
/**
* Set supported message library (owner only)
* @param tx - The transaction to add the move call to
* @param ownerCap - The owner capability object ID
* @param messageLib - The message library address
* @param supported - Whether to support or remove support for the message library
*/
setSupportedMessageLibMoveCall(
tx: Transaction,
ownerCap: string | TransactionArgument,
messageLib: string | TransactionArgument,
supported: boolean | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_supported_message_lib'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, ownerCap),
asAddress(tx, messageLib),
asBool(tx, supported),
],
})
}
/**
* Set allowlist status for an OApp sender (owner only)
* @param tx - The transaction to add the move call to
* @param ownerCap - The owner capability object ID
* @param oapp - The OApp address
* @param allowed - Whether to allow or remove from allowlist
*/
setAllowlistMoveCall(
tx: Transaction,
ownerCap: string | TransactionArgument,
oapp: string | TransactionArgument,
allowed: boolean | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_allowlist'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, ownerCap),
asAddress(tx, oapp),
asBool(tx, allowed),
],
})
}
/**
* Set denylist status for an OApp sender (owner only)
* @param tx - The transaction to add the move call to
* @param ownerCap - The owner capability object ID
* @param oapp - The OApp address
* @param denied - Whether to deny or remove from denylist
*/
setDenylistMoveCall(
tx: Transaction,
ownerCap: string | TransactionArgument,
oapp: string | TransactionArgument,
denied: boolean | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_denylist'),
arguments: [
tx.object(this.objects.executor),
asObject(tx, ownerCap),
asAddress(tx, oapp),
asBool(tx, denied),
],
})
}
/**
* Set worker paused state (owner only)
* @param tx - The transaction to add the move call to
* @param ownerCap - The owner capability object ID
* @param paused - Whether to pause or unpause the worker
*/
setPausedMoveCall(
tx: Transaction,
ownerCap: string | TransactionArgument,
paused: boolean | TransactionArgument
): void {
tx.moveCall({
target: this.#target('set_paused'),
arguments: [tx.object(this.objects.executor), asObject(tx, ownerCap), asBool(tx, paused)],
})
}
// === View Functions ===
/**
* Get the size of the allowlist
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the allowlist size
*/
allowlistSizeMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('allowlist_size'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get the size of the allowlist
* @returns Promise<bigint> - The number of addresses in the allowlist
*/
async allowlistSize(): Promise<bigint> {
return executeSimulate(
this.client,
(tx) => {
this.allowlistSizeMoveCall(tx)
},
(result) => BigInt(bcs.U64.parse(result[0].value))
)
}
/**
* Get default multiplier basis points
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the default multiplier bps
*/
defaultMultiplierBpsMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('default_multiplier_bps'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get default multiplier basis points
* @returns Promise<number> - The default multiplier in basis points
*/
async defaultMultiplierBps(): Promise<number> {
return executeSimulate(
this.client,
(tx) => {
this.defaultMultiplierBpsMoveCall(tx)
},
(result) => bcs.U16.parse(result[0].value)
)
}
/**
* Get executor deposit address for fee collection
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the deposit address
*/
depositAddressMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('deposit_address'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get executor deposit address for fee collection
* @returns Promise<string> - The deposit address
*/
async depositAddress(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.depositAddressMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get destination configuration for executor
* @param tx - The transaction to add the move call to
* @param dstEid - Destination endpoint ID
* @returns Transaction result containing the destination configuration
*/
dstConfigMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('dst_config'),
arguments: [tx.object(this.objects.executor), asU32(tx, dstEid)],
})
}
/**
* Get destination configuration for executor
* @param dstEid - Destination endpoint ID
* @returns Promise<DstConfig> - The destination configuration
*/
async dstConfig(dstEid: number): Promise<DstConfig> {
return executeSimulate(
this.client,
(tx) => {
this.dstConfigMoveCall(tx, dstEid)
},
(result) => this.parseDstConfig(result[0].value)
)
}
/**
* Check if an address has ACL (Access Control List) permission
* @param tx - The transaction to add the move call to
* @param account - The account address to check
* @returns Transaction result containing the ACL permission status
*/
hasAclMoveCall(tx: Transaction, account: string): TransactionResult {
return tx.moveCall({
target: this.#target('has_acl'),
arguments: [tx.object(this.objects.executor), asAddress(tx, account)],
})
}
/**
* Check if an address has ACL (Access Control List) permission
* @param account - The account address to check
* @returns Promise<boolean> - True if the address has ACL permission
*/
async hasAcl(account: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.hasAclMoveCall(tx, account)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Get all registered executor admins
* @param tx - The transaction to add the move call to
* @returns Transaction result containing array of admin addresses
*/
adminsMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('admins'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get all registered executor admins
* @returns Promise<string[]> - Array of admin addresses
*/
async admins(): Promise<string[]> {
return executeSimulate(
this.client,
(tx) => {
this.adminsMoveCall(tx)
},
(result) => {
const parsed = bcs.vector(bcs.Address).parse(result[0].value)
return parsed
}
)
}
/**
* Check if an admin cap is valid (sync Move call)
* @param tx - The transaction to add the move call to
* @param adminCap - The admin capability object ID to check
* @returns TransactionResult - Result containing admin status
*/
isAdminMoveCall(tx: Transaction, adminCap: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('is_admin'),
arguments: [tx.object(this.objects.executor), asObject(tx, adminCap)],
})
}
/**
* Check if an admin cap is valid (async simulation)
* @param adminCap - The admin capability object ID to check
* @returns Promise<boolean> - True if the admin cap is valid
*/
async isAdmin(adminCap: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isAdminMoveCall(tx, adminCap)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Check if an address is an admin
* @param tx - The transaction to add the move call to
* @param admin - The admin address to check
* @returns Transaction result containing the admin status
*/
isAdminAddressMoveCall(tx: Transaction, admin: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('is_admin_address'),
arguments: [tx.object(this.objects.executor), asAddress(tx, admin)],
})
}
/**
* Check if an address is an admin
* @param admin - The admin address to check
* @returns Promise<boolean> - True if the address is an admin
*/
async isAdminAddress(admin: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isAdminAddressMoveCall(tx, admin)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Check if a message library is supported by this executor
* @param tx - The transaction to add the move call to
* @param messageLib - The message library address to check
* @returns Transaction result containing the support status
*/
isSupportedMessageLibMoveCall(tx: Transaction, messageLib: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('is_supported_message_lib'),
arguments: [tx.object(this.objects.executor), asAddress(tx, messageLib)],
})
}
/**
* Check if a message library is supported by this executor
* @param messageLib - The message library address to check
* @returns Promise<boolean> - True if the message library is supported
*/
async isSupportedMessageLib(messageLib: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isSupportedMessageLibMoveCall(tx, messageLib)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Check if an address is allowlisted
* @param tx - The transaction to add the move call to
* @param account - The account address to check
* @returns Transaction result containing the allowlist status
*/
isAllowlistedMoveCall(tx: Transaction, account: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('is_allowlisted'),
arguments: [tx.object(this.objects.executor), asAddress(tx, account)],
})
}
/**
* Check if an address is in the allowlist
* @param account - The account address to check
* @returns Promise<boolean> - True if the address is allowlisted
*/
async isAllowlisted(account: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isAllowlistedMoveCall(tx, account)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Check if an address is denylisted
* @param tx - The transaction to add the move call to
* @param account - The account address to check
* @returns Transaction result containing the denylist status
*/
isDenylistedMoveCall(tx: Transaction, account: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('is_denylisted'),
arguments: [tx.object(this.objects.executor), asAddress(tx, account)],
})
}
/**
* Check if an address is in the denylist
* @param account - The account address to check
* @returns Promise<boolean> - True if the address is denylisted
*/
async isDenylisted(account: string): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isDenylistedMoveCall(tx, account)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Check if executor worker is paused
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the paused status
*/
isPausedMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('is_paused'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Check if executor worker is paused
* @returns Promise<boolean> - True if the worker is paused
*/
async isPaused(): Promise<boolean> {
return executeSimulate(
this.client,
(tx) => {
this.isPausedMoveCall(tx)
},
(result) => bcs.Bool.parse(result[0].value)
)
}
/**
* Get executor price feed address
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the price feed address
*/
priceFeedMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('price_feed'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get executor price feed address
* @returns Promise<string> - The price feed address
*/
async priceFeed(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.priceFeedMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get supported option types for a destination EID
* @param tx - The transaction to add the move call to
* @param dstEid - Destination endpoint ID
* @returns Transaction result containing supported option types
*/
supportedOptionTypesMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('supported_option_types'),
arguments: [tx.object(this.objects.executor), asU32(tx, dstEid)],
})
}
/**
* Get supported option types for a destination EID
* @param dstEid - Destination endpoint ID
* @returns Promise<number[]> - Array of supported option types as bytes
*/
async supportedOptionTypes(dstEid: number): Promise<number[]> {
return executeSimulate(
this.client,
(tx) => {
this.supportedOptionTypesMoveCall(tx, dstEid)
},
(result) => {
const parsed = bcs.vector(bcs.u8()).parse(result[0].value)
return Array.from(parsed)
}
)
}
/**
* Get executor worker capability address
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the worker capability address
*/
workerCapAddressMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('worker_cap_address'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get executor worker capability address
* @returns Promise<string> - The worker capability address
*/
async workerCapAddress(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.workerCapAddressMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get executor worker fee library address
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the worker fee library address
*/
workerFeeLibMoveCall(tx: Transaction): TransactionResult {
return tx.moveCall({
target: this.#target('worker_fee_lib'),
arguments: [tx.object(this.objects.executor)],
})
}
/**
* Get executor worker fee library address
* @returns Promise<string> - The worker fee library address
*/
async workerFeeLib(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.workerFeeLibMoveCall(tx)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get admin capability ID from admin address
* @param tx - The transaction to add the move call to
* @param admin - The admin address
* @returns Transaction result containing the admin capability ID
*/
adminCapIdMoveCall(tx: Transaction, admin: string | TransactionArgument): TransactionResult {
return tx.moveCall({
target: this.#target('admin_cap_id'),
arguments: [tx.object(this.objects.executor), asAddress(tx, admin)],
})
}
/**
* Get admin capability ID from admin address
* @param admin - The admin address
* @returns Promise<string> - The admin capability ID
*/
async adminCapId(admin: string): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.adminCapIdMoveCall(tx, admin)
},
(result) => bcs.Address.parse(result[0].value)
)
}
/**
* Get Executor object address from worker registry using this Executor's worker capability (as a move call)
* This function chains Move calls to decode worker info and extract the Executor object address
* @param tx - The transaction to add the move call to
* @returns Transaction result containing the Executor object address
*/
getExecutorObjectAddressMoveCall(tx: Transaction): TransactionResult {
// Step 1: Get this Executor's worker capability address
const workerCapAddress = this.workerCapAddressMoveCall(tx)
// Step 2: Get worker info bytes from registry
const workerInfoBytes = this.moduleManager
.getWorkerRegistry(this.client)
.getWorkerInfoMoveCall(tx, workerCapAddress)
// Step 3: Decode worker info using worker_common::worker_info_v1::decode
const workerInfo = tx.moveCall({
target: `${this.moduleManager.packages.workerCommon}::worker_info_v1::decode`,
arguments: [workerInfoBytes],
})
// Step 4: Extract worker_info field from decoded WorkerInfoV1
const executorInfoBytes = tx.moveCall({
target: `${this.moduleManager.packages.workerCommon}::worker_info_v1::worker_info`,
arguments: [workerInfo],
})
// Step 5: Decode Executor info using executor::executor_info_v1::decode
const executorInfo = tx.moveCall({
target: `${this.packageId}::executor_info_v1::decode`,
arguments: [executorInfoBytes],
})
// Step 6: Extract executor_object address from decoded ExecutorInfoV1
return tx.moveCall({
target: `${this.packageId}::executor_info_v1::executor_object`,
arguments: [executorInfo],
})
}
/**
* Get Executor object address from worker registry using this Executor's worker capability
* This function uses Move calls to decode worker info and extract the Executor object address
* @returns Promise<string> - The Executor object address
* @throws Will throw an error if worker info is not found or if decoding fails
*/
async getExecutorObjectAddress(): Promise<string> {
return executeSimulate(
this.client,
(tx) => {
this.getExecutorObjectAddressMoveCall(tx)
},
(result) => {
// The result is the Executor object address directly from the Move call chain
return bcs.Address.parse(result[0].value)
}
)
}
// === Private Helper Functions ===
private parseDstConfig(data: Uint8Array): DstConfig {
const config = DstConfigBcs.parse(data)
return {
lzReceiveBaseGas: BigInt(config.lz_receive_base_gas),
lzComposeBaseGas: BigInt(config.lz_compose_base_gas),
multiplierBps: config.multiplier_bps,
floorMarginUsd: BigInt(config.floor_margin_usd),
nativeCap: BigInt(config.native_cap),
}
}
/**
* Generate the full target path for move calls
* @param name - The function name to call
* @param module_name - The module name (defaults to MODULE_NAME)
* @returns The full module path for the move call
* @private
*/
#target(name: string, module_name = MODULE_NAME): string {
return `${this.packageId}::${module_name}::${name}`
}
}