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

# LayerZero Lane

> Stargate v2 OFT cross-chain transfer over LayerZero v2

# LayerZero Lane (Stargate v2 over LayerZero v2)

<Warning>
  **Stargate is Pro (commercial) — not in the OSS build.** The Stargate lane (`exec.stargate.send` + `exec.stargate.dst_observe`), its registry/admin surface, and its tooling all ship only under the commercial license and are excluded from the AGPL-3.0-or-later OSS distribution. If you are running the OSS build, the Stargate lane described below is unavailable — use the **FREE USDT0 native-OFT lane** (`exec.usdt0.send`) for LayerZero-based transfers (see [USDT0 below](#usdt0-the-free-native-oft-lane)).
</Warning>

## What is the LayerZero Lane?

QTG's LayerZero lane is built on **Stargate v2** — a unified-liquidity bridge that uses **LayerZero v2** as its underlying messaging layer. From the executor's point of view:

* **Stargate** owns the liquidity pools, the `sendToken()` entrypoint, and the destination-side `OFTReceived` event.
* **LayerZero v2** owns the message delivery: DVNs verify, Executor delivers, EndpointV2 routes.

The lane supports both **ERC20 OFT pools** (USDC, USDT) and **native asset pools** (ETH, AVAX, ...) — the latter requires no `transferFrom`, just `msg.value`.

## Operator flow: configure, approve, execute, observe, recover, stop

1. **Configure** the commercial Pro deployment, `MG_STARGATE_ENABLED`, required EVM RPC endpoints, and the approved pool/path registry.
2. **Approve** the movement through QTG's normal approval controls and preserve signer identity and allowlist evidence.
3. **Execute** only after the route gates and quote checks pass.
4. **Observe** source receipt and destination `OFTReceived` evidence.
5. **Recover** from recorded intent and chain evidence; do not infer a missing broadcast as success.
6. **Stop** for operator review on ambiguous or failed recovery evidence.

```mermaid theme={null}
flowchart LR
    A["Source Chain<br/>Stargate Pool"] -->|"①sendToken()<br/>OFT call"| EP1["LayerZero<br/>EndpointV2 (src)"]
    EP1 -->|"emits<br/>OFTSent"| DVN["DVN(s)<br/>verify"]
    DVN --> EXEC["Executor<br/>delivers"]
    EXEC --> EP2["LayerZero<br/>EndpointV2 (dst)"]
    EP2 -->|"②lzReceive()"| B["Stargate Pool<br/>(dest)"]
    B -->|"emits<br/>OFTReceived"| RCPT["Recipient Wallet"]

    style A fill:#ffccbc
    style DVN fill:#fff9c4
    style EXEC fill:#ffe0b2
    style B fill:#c8e6c9
    style RCPT fill:#bbdefb
```

## Canonical EndpointV2

LayerZero v2 deploys EndpointV2 to the **same CREATE2 address on every supported chain**:

```
0x1a44076050125825900E736c501f859c50fE728c
```

QTG hardcodes this address and checks every pool's `endpoint()` getter against it during onchain verification. A pool that points at a different endpoint is treated as a mismatch and rejected.

Supported EIDs (LayerZero's per-chain "endpoint ID", not the EVM chain ID):

| EID   | Chain     |
| ----- | --------- |
| 30101 | Ethereum  |
| 30102 | BNB       |
| 30106 | Avalanche |
| 30109 | Polygon   |
| 30110 | Arbitrum  |
| 30111 | Optimism  |
| 30184 | Base      |

## The Stargate lane structure

The Stargate lane (Pro) registers **two executor keys**, not one:

* `exec.stargate.send` — the send-side action that builds and broadcasts the `sendToken` transaction (`pre_submit_check` / `prepare` / `submit`).
* `exec.stargate.dst_observe` — a separate destination-observe step that scans the dest chain for the `OFTReceived` log and resolves the node terminal.

(This differs from CCIP, where a single `exec.ccip.send` key owns both dispatch and observe.) Destination-side *execution* is still performed by the LayerZero Executor, not by QTG — `exec.stargate.dst_observe` only watches for the delivery event.

```mermaid theme={null}
sequenceDiagram
    participant O as Orchestrator
    participant SEND as Stargate Send Lane
    participant REG as Pool Registry
    participant REPO as Intent Store
    participant SIG as Local AWS KMS Signer
    participant SRC as Source Chain RPC
    participant DST as Dest Chain RPC

    Note over O,DST: === pre_submit_check (gating) ===
    O->>SEND: pre_submit_check(ctx)
    SEND->>REG: get pool (chain, asset)
    REG-->>SEND: pool (trust_tier, preapproval_status)
    SEND->>SEND: ensure balance (erc20 / native)
    SEND->>SEND: quote send → native_fee_wei
    SEND->>SEND: native_fee_cap check
    SEND-->>O: pass

    Note over O,DST: === prepare ===
    O->>SEND: prepare(ctx)
    SEND->>SRC: resolve sendToken selector
    SEND->>SEND: encode sendToken calldata
    SEND->>SRC: estimate_gas
    SEND->>SRC: gas_price (+20% bump)
    SEND->>REPO: allocate + persist intent (advisory lock)
    SEND-->>O: PreparedAction(signing_required=true)

    Note over O,DST: === sign + submit ===
    O->>SIG: sign(payload)
    SIG-->>O: SignResult
    O->>SEND: submit(ctx, sign_result)
    SEND->>SRC: eth_sendRawTransaction
    SRC-->>SEND: send_tx_hash
    SEND->>REPO: mark submitting (send_tx_hash)

    Note over O,DST: === observe (= recover) ===
    loop until terminal
        O->>SEND: observe(ctx) → recover(ctx)
        SEND->>SRC: eth_getTransactionReceipt(send_tx_hash)
        alt receipt success
            SEND->>SRC: extract lz_guid from OFTSent log
            SEND->>DST: get_logs(OFTReceived, keyed by lz_guid)
            alt OFTReceived found
                SEND-->>O: COMPLETED
            else still waiting
                SEND-->>O: OBSERVING
            end
        else nonce passed but tx never landed
            SEND->>REPO: mark failed (STARGATE_BROADCAST_LOST)
            SEND-->>O: FAILED
        end
    end
```

## The contract call: sendToken

The signature actually called on the source-side pool:

```solidity theme={null}
sendToken(
    SendParam memory _sendParam,   // (dstEid, to, amountLD, minAmountLD, extraOptions, composeMsg, oftCmd)
    MessagingFee memory _fee,       // (nativeFee, lzTokenFee)
    address _refundAddress
)
```

QTG pins the canonical signature:

```
sendToken(
  (uint32,bytes32,uint256,uint256,bytes,bytes,bytes),
  (uint256,uint256),
  address)
```

The 4-byte selector is resolved at prepare time from this signature rather than stored statically, so an ABI change at the pool would be caught immediately.

### For ERC20 pools

* `_sendParam.amountLD` is the gross amount sent.
* `_sendParam.minAmountLD` is the min the recipient must receive (slippage guard).
* `msg.value = nativeFee` only (the pool pulls tokens via `transferFrom`).
* Allowance check: `_erc20_allowance` must cover `amountLD`.

### For native pools

* The pool consumes native value directly.
* `msg.value = amountLD + nativeFee` (no `transferFrom`).
* Balance check covers `amountLD + nativeFee + gas_budget_wei`.

## Pre-submit gates

`preflight` itself is a no-op — the substantive checks live in `pre_submit_check` and the prepare path:

| Gate                                    | Source                              | Failure error\_code                   |
| --------------------------------------- | ----------------------------------- | ------------------------------------- |
| Pool registered                         | pool registry lookup (chain, asset) | `PRECONDITION_POOL_NOT_REGISTERED`    |
| Pool usable (`trust_tier != "unknown"`) | pool registry                       | `PRECONDITION_POOL_NOT_USABLE`        |
| Preapproval not revoked (ERC20)         | pool preapproval status             | `PRECONDITION_PREAPPROVE_REVOKED`     |
| ERC20 balance sufficient                | source-chain balance read           | `PRECONDITION_INSUFFICIENT_BALANCE`   |
| Native balance sufficient               | source-chain balance read           | (native pools only)                   |
| Quote succeeds                          | Stargate quote                      | propagates RPC error                  |
| Native fee within cap                   | native-fee cap (% of amount)        | `PRECONDITION_NATIVE_FEE_EXCEEDS_CAP` |

The native-fee cap is a percentage of the transferred amount (default `5` = 5%). It's the operator's protection against a DVN/Executor fee spike that would otherwise eat into the moved value.

## Onchain pool verification

Before a pool is allowed in the registry, onchain verification checks four invariants:

```
1. token()          == candidate asset token address  (or a native sentinel)
2. endpoint()       == the canonical LayerZero endpoint for the candidate EID
3. EndpointV2.eid() == candidate EID
4. 1 <= sharedDecimals() <= candidate token decimals
```

Reachability failures (`unreachable`, `empty_code`) are also rejected. The verification is **mandatory** before a pool enters the registry with `trust_tier="trusted"` — an unverified pool sits in `trust_tier="unknown"` and is unreachable by movements.

<Warning>
  The `sharedDecimals()` check rejects `0` and any value exceeding the local token's decimals. A misconfigured pool with `sharedDecimals > localDecimals` would silently truncate large transfers — this is a known LayerZero footgun and QTG refuses to use such a pool.
</Warning>

## Destination-side detection

Stargate's authoritative "the funds arrived" signal is the **`OFTReceived` log** on the dest pool, keyed by the `lz_guid` extracted from the source-side `OFTSent` log. The observer matches on the canonical event topic:

```
OFTReceived(bytes32,uint32,address,uint256)
```

The observer polls dst-chain logs in a bounded window — `3000` blocks lookback (widened from `1000` after a 2026-05-06 drill showed delivery latency exceeding the original window). A match by `lz_guid` resolves the node as `COMPLETED`.

### ERC20 Transfer fallback

If `OFTReceived` indexing lags (operator-instrumented detection), the observer can fall back to scanning ordinary `ERC20 Transfer` events on the dest USDC contract, scoped to the configured `destination_address` and an amount window of `expected_min_amount_received_ld ± slippage`. This fallback is **opt-in** — without `dst_pool_address`, `dst_asset_token_address`, `input_params.destination_address`, and `expected_min_amount_received_ld` all configured, the executor emits a one-time telemetry event `stargate_dst_observe.fallback_disabled` and waits for `OFTReceived` only.

## Recover: the lost-broadcast case

Stargate's `recover` step has one branch that's worth calling out explicitly:

```mermaid theme={null}
flowchart TD
    R[recover] --> S{"intent.status?"}
    S -->|COMPLETED| OK[return COMPLETED]
    S -->|FAILED| F[return FAILED]
    S -->|PREPARED| CRASH["mark failed from prepared<br/>(STARGATE_PRE_BROADCAST_CRASH)"]
    S -->|"SUBMITTING<br/>null send_tx_hash"| DRIFT["terminalize<br/>(data drift)"]
    S -->|"SUBMITTING<br/>has send_tx_hash"| POLL["poll eth_getTransactionReceipt"]
    POLL --> P{"receipt?"}
    P -->|"present, success"| SCAN["scan dst for OFTReceived"]
    P -->|"present, revert"| FAIL[mark failed]
    P -->|"absent + onchain<br/>nonce > intent.nonce"| LOST["3x poll confirmation<br/>then STARGATE_BROADCAST_LOST"]
    P -->|"absent + onchain<br/>nonce ≤ intent.nonce"| W[UNKNOWN, retry 30s]

    style CRASH fill:#ffcdd2
    style FAIL fill:#ffcdd2
    style LOST fill:#ffcdd2
    style OK fill:#c8e6c9
```

The `STARGATE_BROADCAST_LOST` path requires **three independent receipt reads** to come back empty before the lane declares the broadcast lost. This guards against a transient RPC partition: we only conclude the tx is gone when the chain has moved past the reserved nonce **and** the receipt cannot be retrieved.

## Provider refs

On success, the lane returns:

```json theme={null}
{
  "execution_intent_id":   "<uuid>",
  "src_chain_key":         "ethereum",
  "dst_chain_key":         "base",
  "send_tx_hash":          "0x...",
  "lz_guid":               "0x...",
  "amount_received_ld":    "...",
  "dst_pool_address":      "0x..."
}
```

`lz_guid` is the LayerZero-assigned 32-byte message identifier — the canonical cross-chain reference for postmortem, explorer lookups, and operator support.

## USDT0: the FREE native-OFT lane

Stargate is not the only LayerZero application QTG supports. **USDT0** is a standard LayerZero v2 native-OFT lane and ships in the **AGPL-3.0-or-later OSS (Free) build** — registered as `exec.usdt0.send`.

Stargate and USDT0 share the same base LayerZero OFT machinery: EndpointV2 verification, the `OFTSent` / `OFTReceived` log model, and the `lz_guid` correlation key. The split is purely licensing and liquidity model:

|                 | USDT0 (Free)                                       | Stargate (Pro)                                     |
| --------------- | -------------------------------------------------- | -------------------------------------------------- |
| License         | AGPL-3.0-or-later (OSS)                            | Commercial (excluded from OSS)                     |
| Executor key(s) | `exec.usdt0.send`                                  | `exec.stargate.send` + `exec.stargate.dst_observe` |
| OFT model       | native OFT (token *is* the OFT, no liquidity pool) | unified-liquidity pools per asset                  |
| Shared base     | common LayerZero OFT machinery                     | same base                                          |

<Note>
  If you only need the OSS build, USDT0 gives you a working LayerZero v2 transfer lane without any Pro dependency. Stargate adds unified liquidity (USDC/USDT/native ETH-AVAX pools) but requires the commercial license.
</Note>

## Why Stargate, not "raw LayerZero"?

LayerZero v2 is a messaging protocol; sending arbitrary `bytes` cross-chain doesn't move value. **Stargate** is the OFT (Omnichain Fungible Token) application built on top — it provides unified liquidity pools per asset across chains, with `sendToken`/`lzReceive` as the user-facing API.

So when this doc says "LayerZero Lane", what's actually being executed is **Stargate v2** on top of LayerZero v2. The "LayerZero" name applies because the messaging layer, the EndpointV2 contract, and the DVN/Executor trust model all come from LayerZero — Stargate is the asset-handling tier.

If QTG later supports a non-Stargate LayerZero application (e.g. a custom OFT), it would slot into this same lane structure: same EndpointV2 verification, same DVN/Executor trust model, different pool registry entries.

## Related Docs

* [Bridge Lane](/concepts/bridge-lane) — the shared 4-phase pattern
* [CCTP Lane](/concepts/bridges/cctp-lane) — Circle's centralized attestation alternative
* [CCIP Lane](/concepts/bridges/ccip-lane) — Chainlink DON + Risk Management Network
* [Executor Protocol](/concepts/executor-protocol) — preflight/prepare/submit/observe/recover contract
* [State Machine](/concepts/state-machine) — UNKNOWN handling and recovery flow
