> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Sui FAQ

> Common issues and solutions for Sui FAQ. Troubleshoot problems and find answers to frequently asked questions. LayerZero enables secure crosschain messaging.

Frequently asked questions about developing LayerZero applications on Sui.

## General Questions

<AccordionGroup>
  <Accordion title="Why does Sui use the Call (Hot Potato) pattern?">
    Sui Move lacks native dynamic dispatch (unlike EVM's `delegatecall`). The `Call<Param, Result>` pattern provides an alternative by creating structs without `drop` or `store` abilities that must be consumed, using capability-based authorization, and enforcing call sequences through lifecycle states while ensuring atomicity within Programmable Transaction Blocks.

    For a detailed explanation of the Call pattern and Sui's architecture, see the [Sui documentation on PTBs](https://docs.sui.io/concepts/transactions/prog-txn-blocks).
  </Accordion>

  <Accordion title="How is the receive path different from EVM?">
    The key difference is that Sui uses `Call` objects and PTBs instead of `delegatecall`. In EVM, the relayer calls `Endpoint.lzReceive()` which delegates to the OApp. In Sui, the Executor calls `Endpoint.lz_receive()` which creates a `Call<LzReceiveParam, Void>` object that the OApp destroys and processes via explicit PTB routing. Both execution models are permissionless.

    For architectural details, see [Technical Overview](/v2/developers/sui/technical-overview) and [Protocol Overview](/v2/developers/sui/protocol-overview).
  </Accordion>
</AccordionGroup>

## Development Questions

<AccordionGroup>
  <Accordion title="How do I verify my Sui package?">
    Use [SuiScan](https://suiscan.xyz/mainnet/package-verification):

    **Method 1 - Web Interface**:

    1. Navigate to SuiScan verification page
    2. Enter package address
    3. Upload source files
    4. Wait for verification

    **Method 2 - API**:

    ```bash wrap theme={null}
    curl -X POST https://suiscan.xyz/api/verify \
      -d '{"packageId": "0x...", "source": "..."}'
    ```

    See [Sui Guidance](/v2/developers/sui/technical-reference/sui-guidance#contract-verification) for details.
  </Accordion>

  <Accordion title="Do I need to deploy my own Endpoint?">
    No. LayerZero deploys and maintains the `EndpointV2` shared object on Sui. You only need to:

    1. Publish your OApp or OFT package
    2. Register your OApp with the Endpoint (creates a `MessagingChannel`)
    3. Configure pathways to other chains
  </Accordion>

  <Accordion title="How do I handle decimal precision for OFTs?">
    OFTs use **shared decimals** to handle precision differences:

    ```
    Local Decimals:  Token decimals on current chain (e.g., 9)
    Shared Decimals: Crosschain precision (default: 6)

    Conversion Rate: 10^(local - shared)
    ```

    When sending:

    1. Amount is divided by conversion rate (removes dust)
    2. Truncated amount is sent crosschain
    3. Destination multiplies by its conversion rate

    See [OFT Overview](/v2/developers/sui/oft/overview#decimal-precision) for examples.
  </Accordion>

  <Accordion title="Can I use existing tokens with LayerZero?">
    Yes, use an **OFT Adapter** (lock/unlock model):

    ```rust wrap theme={null}
    public struct OFTAdapter {
        escrow: Balance<EXISTING_TOKEN>, // Locked tokens
        // No treasury_cap needed
    }
    ```

    For new tokens, use **mint/burn OFT** for better efficiency.
  </Accordion>
</AccordionGroup>

## Gas and Fees

<AccordionGroup>
  <Accordion title="What are the gas considerations for crosschain messages?">
    Sui uses a dual gas model:

    **Storage Gas**:

    * Charged for creating objects
    * Refunded when objects are deleted
    * Can result in negative net gas

    **Computation Gas**:

    * Charged for execution
    * Not refunded

    **For LayerZero**:

    * Minimum 1000 base gas units
    * Budget 5-20M for typical operations
    * Source chain pays destination execution
  </Accordion>

  <Accordion title="How do I handle negative gas utilization rates?">
    Negative gas is **normal** when storage is freed:

    ```rust wrap theme={null}
    // Freeing storage triggers rebate
    let MyObject { id, data } = obj;
    object::delete(id); // Rebate > gas used
    ```

    **Key Points**:

    * This is not an error
    * Still need minimum 1000 base budget
    * Net cost can be negative
    * Rebate goes to transaction sender

    See [Technical Overview](/v2/developers/sui/technical-overview#gas-model) for details.
  </Accordion>

  <Accordion title="How much should I budget for gas?">
    Recommended gas budgets:

    | Operation          | Gas Budget   |
    | ------------------ | ------------ |
    | Initialize channel | 10,000,000   |
    | Set peer           | 10,000,000   |
    | Configure DVNs     | 15,000,000   |
    | Send message       | 20,000,000   |
    | Receive message    | 15,000,000   |
    | Deploy package     | 100,000,000+ |

    Start higher and reduce based on actual usage.
  </Accordion>
</AccordionGroup>

## Configuration Questions

<AccordionGroup>
  <Accordion title="Do I need to set custom DVNs and Executors?">
    No, defaults are available:

    ```bash wrap theme={null}
    # Minimal configuration (uses defaults)
    initialize_channel(...)  # Required
    set_peer(...)           # Required
    # That's it! Uses default DVNs and Executor
    ```

    Custom configuration is optional for:

    * Specific security requirements
    * Custom DVN sets
    * Private executors
  </Accordion>

  <Accordion title="How do I check my current configuration?">
    Use the TypeScript SDK:

    ```typescript wrap theme={null}
    // Get peer
    const peer = await oapp.getPeer(remoteEid);

    // Get DVN config
    const config = await oapp.getSendConfig(remoteEid);

    console.log({
      peer: Buffer.from(peer).toString('hex'),
      requiredDVNs: config.requiredDVNs,
      optionalDVNs: config.optionalDVNs,
    });
    ```

    The Sui CLI cannot easily parse complex return values.
  </Accordion>

  <Accordion title="Can I change configuration after deployment?">
    Yes, if you retain the `AdminCap`:

    ```bash wrap theme={null}
    # Update peer
    sui client call \
        --function set_peer \
        --args $OAPP $ADMIN_CAP $NEW_EID $NEW_PEER \
        ...

    # Update DVNs
    sui client call \
        --function set_send_uln_config \
        --args $OAPP $ADMIN_CAP $EID ... \
        ...
    ```

    Without AdminCap, configuration is immutable.
  </Accordion>
</AccordionGroup>

## SDKs and Tooling

<AccordionGroup>
  <Accordion title="What SDKs are available for Sui?">
    LayerZero provides two TypeScript SDKs:

    1. **[@layerzerolabs/lz-sui-sdk-v2](https://www.npmjs.com/package/@layerzerolabs/lz-sui-sdk-v2)**

       * Core Endpoint interactions
       * OApp functionality
       * Configuration management

    2. **[@layerzerolabs/lz-sui-oft-sdk-v2](https://www.npmjs.com/package/@layerzerolabs/lz-sui-oft-sdk-v2)**
       * OFT-specific operations
       * Token transfers
       * Balance queries

    See [OFT SDK](/v2/developers/sui/oft/sdk) for usage examples.
  </Accordion>

  <Accordion title="Why can't I use the Sui CLI to query state?">
    The Sui CLI can read simple fields but has limitations for complex queries:

    * Doesn't easily parse return values from view functions
    * Manual decoding needed for bytes arrays and nested structs
    * No built-in formatting for complex types

    **Solution**: Use TypeScript SDK for complex state queries:

    ```typescript wrap theme={null}
    import {SuiClient} from '@mysten/sui.js/client';

    // Query OApp object fields
    const oapp = await client.getObject({
      id: oappObjectId,
      options: {showContent: true},
    });

    // Or use LayerZero SDK helpers
    import {OApp} from '@layerzerolabs/lz-sui-sdk-v2';
    const peer = await oapp.getPeer(client, remoteEid);
    ```
  </Accordion>

  <Accordion title="Is there a deploy/wire tool for Sui?">
    Not currently. Package publication and configuration require:

    1. **Publish packages**: Using `sui client publish`
    2. **Call entry functions**: Invoke configuration functions via `sui client call` or SDK
    3. **Custom scripts**: Write TypeScript scripts for automated workflows

    See [Configuration Guide](/v2/developers/sui/configuration/dvn-executor-config) for manual setup instructions.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title={"Why is my transaction failing with \"unused value without drop ability\"?"}>
    This error means a `Call` object wasn't properly consumed in your PTB:

    ```rust wrap theme={null}
    // - Incorrect: Call object not confirmed
    let call = oapp::send(&mut oapp, &oapp_cap, ...);
    // Transaction ends without destroying call → ERROR

    // - Correct: Call object confirmed
    let call = oapp::send(&mut oapp, &oapp_cap, ...);
    // PTB routes call through Endpoint/ULN/Workers
    let (_, receipt) = oapp::confirm_lz_send(&oapp, &oapp_cap, call);
    ```

    **Solution**: Every `Call` returned must be confirmed/destroyed before the transaction completes.
  </Accordion>

  <Accordion title={"What does \"Channel not initialized\" mean?"}>
    You're trying to send to a destination chain without a `MessagingChannel`:

    ```bash wrap theme={null}
    # Fix: Initialize the channel first
    sui client call \
        --package <ENDPOINT_PACKAGE> \
        --module endpoint_v2 \
        --function init_channel \
        --args <ENDPOINT_OBJECT> <CALL_CAP> <REMOTE_EID> \
        --gas-budget 10000000
    ```

    The Endpoint creates a dedicated `MessagingChannel` shared object for each OApp.
  </Accordion>

  <Accordion title="How do I recover from a stuck message?">
    Use recovery entry functions on the Endpoint (requires `AdminCap`):

    **Skip a message** (increment nonce without execution):

    ```bash wrap theme={null}
    sui client call \
        --package <ENDPOINT_PACKAGE> \
        --module endpoint_v2 \
        --function skip \
        --args <ENDPOINT_OBJECT> <ADMIN_CAP> <OAPP_ADDRESS> <SRC_EID> <SENDER_BYTES32> <NONCE> \
        --gas-budget 10000000
    ```

    **Clear a message** (mark as delivered without execution):

    ```bash wrap theme={null}
    sui client call \
        --function clear \
        --args <ENDPOINT_OBJECT> <ADMIN_CAP> <OAPP_ADDRESS> <SRC_EID> <SENDER_BYTES32> <NONCE> \
        --gas-budget 10000000
    ```

    See [Common Errors](/v2/developers/sui/troubleshooting/common-errors) for more recovery options.
  </Accordion>
</AccordionGroup>

## Security Questions

<AccordionGroup>
  <Accordion title="How do I secure my OApp?">
    Follow these capability-based security practices:

    1. **Validate CallCap in All Functions**:

    ```rust wrap theme={null}
    public fun send(
        self: &OApp,
        oapp_cap: &CallCap,  // - Require capability
        ...
    ) {
        self.assert_oapp_cap(oapp_cap);  // - Validate ownership
        // ...
    }

    fun assert_oapp_cap(self: &OApp, cap: &CallCap) {
        assert!(self.oapp_cap.id() == cap.id(), EInvalidOAppCap);
    }
    ```

    2. **Validate Call Objects**:

    ```rust wrap theme={null}
    public fun lz_receive(self: &mut OApp, call: Call<LzReceiveParam, Void>) {
        // - Validate Call came from authorized Endpoint
        let (callee, param, _) = call.destroy(&self.oapp_cap);
        assert!(callee == endpoint_address(), EOnlyEndpoint);

        // - Validate sender is configured peer
        let peer = self.peer.get_peer(param.src_eid);
        assert!(param.sender == peer, EOnlyPeer);
    }
    ```

    3. **Secure Capability Objects**:

    * Store `CallCap` in package module storage (not transferred)
    * Use multisig or hardware wallet for `AdminCap`
    * Never expose capabilities publicly
    * Transfer `AdminCap` carefully (use `transfer::public_transfer`)

    4. **Protect UpgradeCap**:

    * Keep upgrade authority secure
    * Consider freezing upgrades after deployment (`package::make_immutable`)
    * Use multisig for mainnet upgrade authority
  </Accordion>

  <Accordion title="What are common security pitfalls?">
    * * Missing `CallCap` validation in functions
    * * Not validating `Call` object source (callee address)
    * * Skipping peer validation in `lz_receive`
    * * Losing capability objects (no recovery possible)
    * * Wrong peer addresses configured
    * * Exposing `AdminCap` or `CallCap` publicly

    See [OApp Best Practices](/v2/developers/sui/oapp/overview#best-practices) for details.
  </Accordion>
</AccordionGroup>

## Next Steps

* [Common Errors](/v2/developers/sui/troubleshooting/common-errors)
* [Technical Overview](/v2/developers/sui/technical-overview)
* [Configuration Guide](/v2/developers/sui/configuration/dvn-executor-config)
* [Sui Guidance](/v2/developers/sui/technical-reference/sui-guidance)
