> ## 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.

# IOTA L1 FAQ

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

Frequently asked questions about developing LayerZero applications on IOTA L1.

## General Questions

<AccordionGroup>
  <Accordion title="Why does IOTA use the Call (Hot Potato) pattern?">
    IOTA 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 IOTA's architecture, see the [IOTA documentation on PTBs](https://docs.iota.org/developer/iota-101/transactions/ptb/programmable-transaction-blocks).
  </Accordion>

  <Accordion title="How is the receive path different from EVM?">
    The key difference is that IOTA uses `Call` objects and PTBs instead of `delegatecall`. In EVM, the relayer calls `Endpoint.lzReceive()` which delegates to the OApp. In IOTA, 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/iota/technical-overview) and [Protocol Overview](/v2/developers/iota/protocol-overview).
  </Accordion>
</AccordionGroup>

## Development Questions

<AccordionGroup>
  <Accordion title="Do I need to deploy my own Endpoint?">
    No. LayerZero deploys and maintains the `EndpointV2` shared object on IOTA L1. 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/iota/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?">
    IOTA 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/iota/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 IOTA 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
    iota client call \
        --function set_peer \
        --args $OAPP $ADMIN_CAP $NEW_EID $NEW_PEER \
        ...

    # Update DVNs
    iota 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 IOTA?">
    LayerZero provides two TypeScript SDKs:

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

       * Core Endpoint interactions
       * OApp functionality
       * Configuration management

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

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

  <Accordion title="Why can't I use the IOTA CLI to query state?">
    The IOTA 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 {IOTAClient} from '@iota/iota-sdk/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-iotal1-sdk-v2';
    const peer = await oapp.getPeer(client, remoteEid);
    ```
  </Accordion>

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

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

    See [Configuration Guide](/v2/developers/iota/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
    iota 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}
    iota 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}
    iota client call \
        --function clear \
        --args <ENDPOINT_OBJECT> <ADMIN_CAP> <OAPP_ADDRESS> <SRC_EID> <SENDER_BYTES32> <NONCE> \
        --gas-budget 10000000
    ```

    See [Common Errors](/v2/developers/iota/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/iota/oapp/overview#best-practices) for details.
  </Accordion>
</AccordionGroup>

## Next Steps

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