import { bcs } from '@mysten/sui/bcs'
import { SuiClient } from '@mysten/sui/client'
import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions'

import { Bytes32Bcs, DvnDstConfigBcs } from '../../bcs'
import { ModuleManager } from '../../module-manager'
import { DvnDstConfig, ObjectOptions } from '../../types'
import {
    asAddress,
    asBool,
    asBytes,
    asBytes32,
    asObject,
    asU128,
    asU16,
    asU256,
    asU32,
    asU64,
    executeSimulate,
} from '../../utils'

const MODULE_NAME = 'dvn'

export const DVNErrorCode = {
    // DVN related errors (with DVN_ prefix)
    DVN_EExpiredSignature: 1,
    DVN_EEidNotSupported: 2,
    DVN_EHashAlreadyUsed: 3,
    DVN_EPtbBuilderAlreadyInitialized: 4,

    // Multisig related errors (matching multisig.move)
    Multisig_EDuplicatedSigner: 1,
    Multisig_EInvalidSignatureLength: 2,
    Multisig_EInvalidSignerLength: 3,
    Multisig_EQuorumIsZero: 4,
    Multisig_ESignaturesLessThanQuorum: 5,
    Multisig_ESignerAlreadyExists: 6,
    Multisig_ESignerNotFound: 7,
    Multisig_ESignerNotInCommittee: 8,
    Multisig_ESignersSizeIsLessThanQuorum: 9,

    // DVNInfoV1 related errors (matching dvn_info_v1.move)
    DVNInfoV1_EInvalidData: 1,
    DVNInfoV1_EInvalidVersion: 2,
} as const

export class DVN {
    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 ===

    /**
     * Set admin role for an address (admin only)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param admin - The admin address or transaction argument
     * @param active - Whether to activate or deactivate the admin role or transaction argument
     */
    setAdminMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        admin: string | TransactionArgument,
        active: boolean | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_admin'),
            arguments: [tx.object(this.objects.dvn), asObject(tx, adminCap), asAddress(tx, admin), asBool(tx, active)],
        })
    }

    /**
     * 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 or transaction argument
     */
    setDefaultMultiplierBpsMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        multiplierBps: number | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_default_multiplier_bps'),
            arguments: [tx.object(this.objects.dvn), asObject(tx, adminCap), asU16(tx, multiplierBps)],
        })
    }

    /**
     * Set deposit address for DVN 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 or transaction argument
     */
    setDepositAddressMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        depositAddress: string | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_deposit_address'),
            arguments: [tx.object(this.objects.dvn), asObject(tx, adminCap), asAddress(tx, depositAddress)],
        })
    }

    /**
     * Set price feed for DVN (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 or transaction argument
     */
    setPriceFeedMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        priceFeed: string | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_price_feed'),
            arguments: [tx.object(this.objects.dvn), asObject(tx, adminCap), asAddress(tx, priceFeed)],
        })
    }

    /**
     * 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 types
     */
    setSupportedOptionTypesMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        dstEid: number | TransactionArgument,
        optionTypes: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_supported_option_types'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asU32(tx, dstEid),
                asBytes(tx, optionTypes),
            ],
        })
    }

    /**
     * Set worker fee library (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.dvn), asObject(tx, adminCap), asAddress(tx, workerFeeLib)],
        })
    }

    /**
     * Set destination configuration for DVN (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 gas - Gas amount for verification
     * @param multiplierBps - Multiplier in basis points
     * @param floorMarginUsd - Floor margin in USD
     */
    setDstConfigMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        dstEid: number | TransactionArgument,
        gas: bigint | number | string | TransactionArgument,
        multiplierBps: number | TransactionArgument,
        floorMarginUsd: bigint | number | string | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_dst_config'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asU32(tx, dstEid),
                asU256(tx, gas),
                asU16(tx, multiplierBps),
                asU128(tx, floorMarginUsd),
            ],
        })
    }

    /**
     * Initialize PTB builder move calls for DVN worker operations (admin only) - can only be called once
     * @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 init PTB builder call
     */
    initPtbBuilderMoveCallsMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        targetPtbBuilder: string | TransactionArgument,
        getFeeMoveCalls: TransactionArgument,
        assignJobMoveCalls: TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('init_ptb_builder_move_calls'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, targetPtbBuilder),
                getFeeMoveCalls, // First element of DVN PTB result tuple
                assignJobMoveCalls, // Second element of DVN PTB result tuple
            ],
        })
    }

    /**
     * Set PTB builder move calls for DVN worker operations (admin with signatures)
     * @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
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     * @returns Transaction result containing the set PTB builder call
     */
    setPtbBuilderMoveCallsMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        targetPtbBuilder: string | TransactionArgument,
        getFeeMoveCalls: TransactionArgument,
        assignJobMoveCalls: TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('set_ptb_builder_move_calls'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, targetPtbBuilder),
                getFeeMoveCalls, // First element of DVN PTB result tuple
                assignJobMoveCalls, // Second element of DVN PTB result tuple
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    // === Set Functions with Signatures ===

    /**
     * Set supported message library (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param messageLib - The message library address
     * @param supported - Whether to support or remove support for the message library
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setSupportedMessageLibMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        messageLib: string | TransactionArgument,
        supported: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_supported_message_lib'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, messageLib),
                asBool(tx, supported),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set allowlist status for an OApp sender (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param oapp - The OApp address to allowlist/remove
     * @param allowed - Whether to allow or remove from allowlist
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setAllowlistMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        oapp: string | TransactionArgument,
        allowed: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_allowlist'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, oapp),
                asBool(tx, allowed),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set denylist status for an OApp sender (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param oapp - The OApp address to denylist/remove
     * @param denied - Whether to deny or remove from denylist
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setDenylistMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        oapp: string | TransactionArgument,
        denied: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_denylist'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, oapp),
                asBool(tx, denied),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set paused state with multisig signatures (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param paused - Whether to pause or unpause the DVN
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setPausedMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        paused: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_paused'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asBool(tx, paused),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set quorum threshold with multisig signatures (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param quorum - The new quorum threshold
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setQuorumMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        quorum: bigint | number | string | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_quorum'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asU64(tx, quorum),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set DVN signer with multisig signatures (admin with signatures)
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param signer - The signer public key as bytes
     * @param active - Whether to activate or deactivate the signer
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    setDvnSignerMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        signer: Uint8Array | TransactionArgument,
        active: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('set_dvn_signer'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asBytes(tx, signer),
                asBool(tx, active),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Change admin status using quorum signatures
     * @param tx - The transaction to add the move call to
     * @param admin - The admin address to change
     * @param active - Whether to activate or deactivate the admin
     * @param expiration - Signature expiration timestamp
     * @param signatures - Multisig signatures for authorization
     */
    quorumChangeAdminMoveCall(
        tx: Transaction,
        admin: string | TransactionArgument,
        active: boolean | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('quorum_change_admin'),
            arguments: [
                tx.object(this.objects.dvn),
                asAddress(tx, admin),
                asBool(tx, active),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    // Verification Functions

    /**
     * Verify a packet with DVN signatures (admin with signatures) - returns Call object for ULN302
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param targetMessageLib - The target message library address (usually ULN302)
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     * @param confirmations - Required confirmations count
     * @param expiration - Signature expiration timestamp
     * @param signatures - DVN multisig signatures
     * @returns Transaction result containing the verification Call object
     */
    verifyMoveCall(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        targetMessageLib: string | TransactionArgument,
        packetHeader: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument,
        confirmations: bigint | number | string | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('verify'),
            arguments: [
                tx.object(this.objects.dvn),
                asObject(tx, adminCap),
                asAddress(tx, targetMessageLib),
                asBytes(tx, packetHeader),
                asBytes32(tx, payloadHash, this.moduleManager.getUtils()),
                asU64(tx, confirmations),
                asU64(tx, expiration),
                asBytes(tx, signatures),
                tx.object.clock(),
            ],
        })
    }

    // Worker Functions

    /**
     * Assign verification job for DVN (called via PTB with MultiCall created by send function in ULN302)
     * @param tx - The transaction to add the move call to
     * @param dvnMultiCall - The DVN multi-call transaction result
     * @returns Transaction result containing a Call for fee calculation
     */
    assignJobMoveCall(tx: Transaction, dvnMultiCall: TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('assign_job'),
            arguments: [tx.object(this.objects.dvn), dvnMultiCall],
        })
    }

    /**
     * Confirm assign job operation with fee calculation
     * @param tx - The transaction to add the move call to
     * @param dvnMultiCall - The DVN multi-call transaction result
     * @param feelibCall - The fee library call transaction result
     */
    confirmAssignJobMoveCall(
        tx: Transaction,
        dvnMultiCall: TransactionArgument,
        feelibCall: TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('confirm_assign_job'),
            arguments: [tx.object(this.objects.dvn), dvnMultiCall, feelibCall],
        })
    }

    /**
     * Get fee for verification (using MultiCall created by quote function in ULN302)
     * @param tx - The transaction to add the move call to
     * @param dvnMultiCall - The DVN multi-call transaction result
     * @returns Transaction result containing a Call for fee calculation
     */
    getFeeMoveCall(tx: Transaction, dvnMultiCall: TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_fee'),
            arguments: [tx.object(this.objects.dvn), dvnMultiCall],
        })
    }

    /**
     * Confirm get fee operation with fee calculation
     * @param tx - The transaction to add the move call to
     * @param dvnMultiCall - The DVN multi-call transaction result
     * @param feelibCall - The fee library call transaction result
     */
    confirmGetFeeMoveCall(tx: Transaction, dvnMultiCall: TransactionArgument, feelibCall: TransactionArgument): void {
        tx.moveCall({
            target: this.#target('confirm_get_fee'),
            arguments: [tx.object(this.objects.dvn), dvnMultiCall, feelibCall],
        })
    }

    // === 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.dvn)],
        })
    }

    /**
     * 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.dvn)],
        })
    }

    /**
     * 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 DVN 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.dvn)],
        })
    }

    /**
     * Get DVN 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 DVN
     * @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.dvn), asU32(tx, dstEid)],
        })
    }

    /**
     * Get destination configuration for DVN
     * @param dstEid - Destination endpoint ID
     * @returns Promise<DvnDstConfig> - The destination configuration
     */
    async dstConfig(dstEid: number): Promise<DvnDstConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.dstConfigMoveCall(tx, dstEid)
            },
            (result) => {
                const value = DvnDstConfigBcs.parse(result[0].value)
                return {
                    gas: BigInt(value.gas),
                    multiplierBps: value.multiplier_bps,
                    floorMarginUsd: BigInt(value.floor_margin_usd),
                }
            }
        )
    }

    /**
     * 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 | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('has_acl'),
            arguments: [tx.object(this.objects.dvn), 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 DVN 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.dvn)],
        })
    }

    /**
     * Get all registered DVN 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
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID to check
     * @returns Transaction result containing the admin status
     */
    isAdminMoveCall(tx: Transaction, adminCap: string | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('is_admin'),
            arguments: [tx.object(this.objects.dvn), asObject(tx, adminCap)],
        })
    }

    /**
     * Check if an admin cap is valid
     * @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.dvn), 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 DVN
     * @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.dvn), asAddress(tx, messageLib)],
        })
    }

    /**
     * Check if a message library is supported by this DVN
     * @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.dvn), 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.dvn), 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 DVN 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.dvn)],
        })
    }

    /**
     * Check if DVN 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)
        )
    }

    /**
     * Check if an address is a registered DVN signer
     * @param tx - The transaction to add the move call to
     * @param signer - The signer public key as bytes
     * @returns Transaction result containing the signer status
     */
    isSignerMoveCall(tx: Transaction, signer: Uint8Array | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('is_signer'),
            arguments: [tx.object(this.objects.dvn), asBytes(tx, signer)],
        })
    }

    /**
     * Check if an address is a registered DVN signer
     * @param signer - The signer public key as bytes
     * @returns Promise<boolean> - True if the address is a registered signer
     */
    async isSigner(signer: Uint8Array): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.isSignerMoveCall(tx, signer)
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Get DVN 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.dvn)],
        })
    }

    /**
     * Get DVN 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 DVN quorum threshold
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the quorum threshold
     */
    quorumMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('quorum'),
            arguments: [tx.object(this.objects.dvn)],
        })
    }

    /**
     * Get DVN quorum threshold
     * @returns Promise<bigint> - The quorum threshold
     */
    async quorum(): Promise<bigint> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.quorumMoveCall(tx)
            },
            (result) => BigInt(bcs.U64.parse(result[0].value))
        )
    }

    /**
     * Get the total count of registered DVN signers
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the signer count
     */
    signerCountMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('signer_count'),
            arguments: [tx.object(this.objects.dvn)],
        })
    }

    /**
     * Get the total count of registered DVN signers
     * @returns Promise<bigint> - The number of registered signers
     */
    async signerCount(): Promise<bigint> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.signerCountMoveCall(tx)
            },
            (result) => BigInt(bcs.U64.parse(result[0].value))
        )
    }

    /**
     * Get all registered DVN signers
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing array of signer public keys
     */
    signersMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('signers'),
            arguments: [tx.object(this.objects.dvn)],
        })
    }

    /**
     * Get all registered DVN signers
     * @returns Promise<Uint8Array[]> - Array of signer public keys as byte arrays
     */
    async signers(): Promise<Uint8Array[]> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.signersMoveCall(tx)
            },
            (result) => {
                const parsed = bcs.vector(bcs.vector(bcs.u8())).parse(result[0].value)
                return parsed.map((signer) => Buffer.from(signer))
            }
        )
    }

    /**
     * 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.dvn), asU32(tx, dstEid)],
        })
    }

    /**
     * Get supported option types for a destination EID
     * @param dstEid - Destination endpoint ID
     * @returns Promise<Uint8Array> - Array of supported option types as bytes
     */
    async supportedOptionTypes(dstEid: number): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.supportedOptionTypesMoveCall(tx, dstEid)
            },
            (result) => {
                const parsed = bcs.vector(bcs.u8()).parse(result[0].value)
                return Buffer.from(parsed)
            }
        )
    }

    /**
     * Get DVN Verifier ID (VID)
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the VID
     */
    vidMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('vid'),
            arguments: [tx.object(this.objects.dvn)],
        })
    }

    /**
     * Get DVN VID (Verifier ID)
     * @returns Promise<number> - The DVN verifier ID
     */
    async vid(): Promise<number> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.vidMoveCall(tx)
            },
            (result) => bcs.U32.parse(result[0].value)
        )
    }

    /**
     * Get DVN 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.dvn)],
        })
    }

    /**
     * Get DVN 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 DVN 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.dvn)],
        })
    }

    /**
     * Get DVN 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.dvn), 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)
        )
    }

    /**
     * Check if PTB builder has been initialized
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the PTB builder initialization status
     */
    isPtbBuilderInitializedMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('is_ptb_builder_initialized'),
            arguments: [tx.object(this.objects.dvn)],
        })
    }

    /**
     * Check if PTB builder has been initialized
     * @returns Promise<boolean> - True if the PTB builder has been initialized
     */
    async isPtbBuilderInitialized(): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.isPtbBuilderInitializedMoveCall(tx)
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Get DVN object address from worker registry using this DVN's worker capability (as a move call)
     * This function chains Move calls to decode worker info and extract the DVN object address
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the DVN object address
     */
    getDvnObjectAddressMoveCall(tx: Transaction): TransactionResult {
        // Step 1: Get this DVN'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 dvnInfoBytes = tx.moveCall({
            target: `${this.moduleManager.packages.workerCommon}::worker_info_v1::worker_info`,
            arguments: [workerInfo],
        })

        // Step 5: Decode DVN info using dvn::dvn_info_v1::decode
        const dvnInfo = tx.moveCall({
            target: `${this.packageId}::dvn_info_v1::decode`,
            arguments: [dvnInfoBytes],
        })

        // Step 6: Extract dvn_object address from decoded DVNInfoV1
        return tx.moveCall({
            target: `${this.packageId}::dvn_info_v1::dvn_object`,
            arguments: [dvnInfo],
        })
    }

    /**
     * Get DVN object address from worker registry using this DVN's worker capability
     * This function uses Move calls to decode worker info and extract the DVN object address
     * @returns Promise<string> - The DVN object address
     * @throws Will throw an error if worker info is not found or if decoding fails
     */
    async getDvnObjectAddress(): Promise<string> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getDvnObjectAddressMoveCall(tx)
            },
            (result) => {
                // The result is the DVN object address directly from the Move call chain
                return bcs.Address.parse(result[0].value)
            }
        )
    }

    // === Witness Functions ===

    /**
     * Create a LayerZero witness for DVN 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}::dvn_witness::new`,
            arguments: [],
        })
    }

    // === Hash Functions ===

    /**
     * Create a verify hash for packet verification
     * @param tx - The transaction to add the move call to
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     * @param confirmations - Required confirmations count
     * @param target - Target address for verification
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the verify hash
     */
    createVerifyHashMoveCall(
        tx: Transaction,
        packetHeader: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument,
        confirmations: bigint | number | string | TransactionArgument,
        target: string | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_verify_hash', 'hashes'),
            arguments: [
                asBytes(tx, packetHeader),
                asBytes(tx, payloadHash),
                asU64(tx, confirmations),
                asAddress(tx, target),
                asU32(tx, vid),
                asU64(tx, expiration),
            ],
        })
    }

    /**
     * Create a verify hash for packet verification
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     * @param confirmations - Required confirmations count
     * @param target - Target address for verification
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The verify hash as bytes
     */
    async createVerifyHash(
        packetHeader: Uint8Array,
        payloadHash: Uint8Array,
        confirmations: bigint,
        target: string,
        vid: number,
        expiration: bigint
    ): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createVerifyHashMoveCall(tx, packetHeader, payloadHash, confirmations, target, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set DVN signer hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param signer - The signer public key as bytes
     * @param active - Whether to activate or deactivate the signer
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set DVN signer hash
     */
    createSetDvnSignerHashMoveCall(
        tx: Transaction,
        signer: Uint8Array | TransactionArgument,
        active: boolean | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_dvn_signer_hash', 'hashes'),
            arguments: [asBytes(tx, signer), asBool(tx, active), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a set DVN signer hash for signature verification
     * @param signer - The signer public key as bytes
     * @param active - Whether to activate or deactivate the signer
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The set DVN signer hash as bytes
     */
    async createSetDvnSignerHash(
        signer: Uint8Array,
        active: boolean,
        vid: number,
        expiration: bigint
    ): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createSetDvnSignerHashMoveCall(tx, signer, active, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set quorum hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param quorum - The new quorum threshold
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set quorum hash
     */
    createSetQuorumHashMoveCall(
        tx: Transaction,
        quorum: bigint | number | string | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_quorum_hash', 'hashes'),
            arguments: [asU64(tx, quorum), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a set quorum hash for signature verification
     * @param quorum - The new quorum threshold
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The set quorum hash as bytes
     */
    async createSetQuorumHash(quorum: bigint, vid: number, expiration: bigint): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createSetQuorumHashMoveCall(tx, quorum, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a quorum change admin hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param admin - The admin address to change
     * @param active - Whether to activate or deactivate the admin
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the quorum change admin hash
     */
    createQuorumChangeAdminHashMoveCall(
        tx: Transaction,
        admin: string | TransactionArgument,
        active: boolean | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_quorum_change_admin_hash', 'hashes'),
            arguments: [asAddress(tx, admin), asBool(tx, active), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a quorum change admin hash for signature verification
     * @param admin - The admin address to change
     * @param active - Whether to activate or deactivate the admin
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The quorum change admin hash as bytes
     */
    async createQuorumChangeAdminHash(
        admin: string,
        active: boolean,
        vid: number,
        expiration: bigint
    ): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createQuorumChangeAdminHashMoveCall(tx, admin, active, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set allowlist hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param oapp - The OApp address to allowlist/remove
     * @param allowed - Whether to allow or remove from allowlist
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set allowlist hash
     */
    createSetAllowlistHashMoveCall(
        tx: Transaction,
        oapp: string | TransactionArgument,
        allowed: boolean | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_allowlist_hash', 'hashes'),
            arguments: [asAddress(tx, oapp), asBool(tx, allowed), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a set allowlist hash for signature verification
     * @param oapp - The OApp address to allowlist/remove
     * @param allowed - Whether to allow or remove from allowlist
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The set allowlist hash as bytes
     */
    async createSetAllowlistHash(oapp: string, allowed: boolean, vid: number, expiration: bigint): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createSetAllowlistHashMoveCall(tx, oapp, allowed, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set denylist hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param oapp - The OApp address to denylist/remove
     * @param denied - Whether to deny or remove from denylist
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set denylist hash
     */
    createSetDenylistHashMoveCall(
        tx: Transaction,
        oapp: string | TransactionArgument,
        denied: boolean | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_denylist_hash', 'hashes'),
            arguments: [asAddress(tx, oapp), asBool(tx, denied), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a set denylist hash for signature verification
     * @param oapp - The OApp address to denylist/remove
     * @param denied - Whether to deny or remove from denylist
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The set denylist hash as bytes
     */
    async createSetDenylistHash(oapp: string, denied: boolean, vid: number, expiration: bigint): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createSetDenylistHashMoveCall(tx, oapp, denied, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set pause hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param paused - Whether to pause or unpause the DVN
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set pause hash
     */
    createSetPauseHashMoveCall(
        tx: Transaction,
        paused: boolean | TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_pause_hash', 'hashes'),
            arguments: [asBool(tx, paused), asU32(tx, vid), asU64(tx, expiration)],
        })
    }

    /**
     * Create a set pause hash for signature verification
     * @param paused - Whether to pause or unpause the DVN
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Promise<Uint8Array> - The set pause hash as bytes
     */
    async createSetPauseHash(paused: boolean, vid: number, expiration: bigint): Promise<Uint8Array> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.createSetPauseHashMoveCall(tx, paused, vid, expiration)
            },
            (result) => {
                const bytes32Struct = Bytes32Bcs.parse(result[0].value)
                return Buffer.from(bytes32Struct.bytes)
            }
        )
    }

    /**
     * Create a set PTB builder move calls hash for signature verification
     * @param tx - The transaction to add the move call to
     * @param targetPtbBuilder - Target PTB builder address
     * @param getFeeMoveCalls - Get fee move calls transaction argument
     * @param assignJobMoveCalls - Assign job move calls transaction argument
     * @param vid - Verifier ID
     * @param expiration - Signature expiration timestamp
     * @returns Transaction result containing the set PTB builder move calls hash
     */
    createSetPtbBuilderMoveCallsHashMoveCall(
        tx: Transaction,
        targetPtbBuilder: string | TransactionArgument,
        getFeeMoveCalls: TransactionArgument,
        assignJobMoveCalls: TransactionArgument,
        vid: number | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('create_set_ptb_builder_move_calls_hash', 'hashes'),
            arguments: [
                asAddress(tx, targetPtbBuilder),
                getFeeMoveCalls,
                assignJobMoveCalls,
                asU32(tx, vid),
                asU64(tx, expiration),
            ],
        })
    }

    /**
     * Populate verify with DVN verification and ULN verify call
     * @param tx - The transaction to add the move call to
     * @param adminCap - The admin capability object ID
     * @param targetMessageLib - The target message library address (usually ULN302)
     * @param targetVerification - The verification object address for the target message library
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     * @param confirmations - Required confirmations count
     * @param expiration - Signature expiration timestamp
     * @param signatures - DVN multisig signatures
     */
    populateVerify(
        tx: Transaction,
        adminCap: string | TransactionArgument,
        targetMessageLib: string,
        targetVerification: string,
        packetHeader: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument,
        confirmations: bigint | number | string | TransactionArgument,
        expiration: bigint | number | string | TransactionArgument,
        signatures: Uint8Array | TransactionArgument
    ): void {
        // Step 1: Call DVN's verifyMoveCall to get the verification call
        const dvnCall = this.verifyMoveCall(
            tx,
            adminCap,
            targetMessageLib,
            packetHeader,
            payloadHash,
            confirmations,
            expiration,
            signatures
        )

        // Step 2: Get the message library object address based on targetMessageLib
        const messageLibObjectAddress = this.#getUlnObjectAddress(targetMessageLib)

        tx.moveCall({
            target: this.#getUlnVerifyTarget(targetMessageLib),
            arguments: [tx.object(messageLibObjectAddress), asObject(tx, targetVerification), dvnCall],
        })
    }

    /**
     * Get message library object address based on targetMessageLib package address
     * @param targetMessageLib - The target message library package address
     * @returns Message library object address
     * @throws Error if targetMessageLib doesn't match any known message library
     * @private
     */
    #getUlnObjectAddress(targetMessageLib: string): string {
        // Check if targetMessageLib equals ULN302 package address
        if (targetMessageLib === this.moduleManager.packages.uln302) {
            return this.objects.uln302
        }

        throw new Error('Target message library not match objects')
    }

    /**
     * Get ULN verify target path based on targetMessageLib package address
     * @param targetMessageLib - The target message library package address
     * @returns Message library verify target path
     * @throws Error if targetMessageLib doesn't match any known message library
     * @private
     */
    #getUlnVerifyTarget(targetMessageLib: string): string {
        if (targetMessageLib === this.moduleManager.packages.uln302) {
            return `${targetMessageLib}::uln_302::verify`
        }

        throw new Error('Target message library not match any target')
    }
    /**
     * 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}`
    }
}
