LPsLux Proposals
Multi-Party Computation
LP-1137

Native Swap Integration on T-Chain, X-Chain, and Z-Chain

Review

Deep-integration blueprint to migrate legacy swaps REST API (swaps.ts) into fully on-chain M/X/Z chain transactions and RPC

Category
Core
Created
2025-07-25

Abstract

This LP provides a deep-integration blueprint to migrate the legacy swaps REST API (app/server/src/routes/swaps.ts) into a fully on-chain workflow across T-Chain, X-Chain, and Z-Chain, eliminating centralized swap servers and off-chain key-managers.

Motivation

The current REST-based bridge back-end suffers from:

  • Centralized swap book in Postgres
  • Off-chain signature collection by a separate worker
  • Polling and cron jobs for finality tracking

We aim to push swap intent, MPC signing, final settlement, and optional privacy entirely on-chain, enabling stateless front-ends and decentralized swap execution.

Specification

1 Legacy API Recap

The existing bridge back-end exposes HTTP routes:

MethodPathPurpose
POST /swapscreate a cross-chain swaprecord DB row, key-manager signs
GET /swaps/:idpoll swap statusPENDING / SIGNED / FINAL
POST /swaps/:id/signprivileged broadcast MPC signature
POST /swaps/:id/finalizemark COMPLETED, emit webhook

2 High-Level Chain Roles

ChainRole for swaps
X-ChainSwapTx locks escrow funds; emits SwapID = txID; user intent
T-ChainSwapSigTx contains aggregate MPC signature + proof of quorum; slashing for non-participation
Z-Chain(optional) privacy layer: zSwapDeposit → commitment; zSwapRedeem → shielded withdrawal proof

3 On-Chain Data Structures

3.1 SwapTx (X-Chain)

MethodPathPurpose
POST /swapscreate a cross-chain swaprecord DB row, key-manager signs
GET /swaps/:idpoll swap statusPENDING / SIGNED / FINAL
POST /swaps/:id/signprivileged broadcast MPC signature
POST /swaps/:id/finalizemark COMPLETED, emit webhook

Pain points:

  1. Swap book in Postgres → trusted central server
  2. Signature collection off-chain in Node.js worker
  3. Finality tracking via polling and cron jobs

Goal: Move this workflow entirely on-chain so front-ends remain stateless.

1 High-Level Chain Roles

ChainRole for swaps
X-ChainSwapTx locks escrow funds; emits SwapID = txID; user intent
T-ChainSwapSigTx contains aggregate MPC signature + proof of quorum; slashing for non-participation
Z-Chain(optional) privacy layer: zSwapDeposit → commitment; zSwapRedeem → shielded withdrawal proof

After signing on T-Chain, X-Chain light-client proof unlocks escrow and relays native transaction. Privacy flag routes via Z-Chain.

2 On-Chain Data Structures

2.1 SwapTx (X-Chain)

type SwapTx struct {
    BaseTx
    SrcChain  uint32
    DstChain  uint32
    SrcAsset  AssetID
    DstAsset  AssetID
    Amount    uint64
    MinOut    uint64
    Expiry    uint64
    FeeBps    uint16
    Recipient [32]byte
}

Funds lock to SwapFx until SwapSigTx appears; SwapID = txID.

2.2 SwapSigTx (T-Chain)

type SwapSigTx struct {
    BaseTx
    SwapID    ids.ID
    MPCAlgo   byte
    Signature []byte
    SigBitmap []byte
    ProofHash [32]byte
}

On seeing SwapTx, validators form threshold signature off-chain and submit SwapSigTx on T-Chain.

3 Transaction Flow

sequenceDiagram
    User->>Wallet: create swap (BTC→wBTC,0.1)
    Wallet-->>X-Chain: broadcast SwapTx
    X-Chain->>T-Chain: watch SwapRequested event
    T-Chain->>MPC Nodes: gather shares
    MPC Nodes-->>T-Chain: threshold signature
    T-Chain-->>T-Chain: submit SwapSigTx
    T-Chain-->>X-Chain: WarpMsg proof
    X-Chain-->>X-Chain: unlock escrow, mark FINAL
    X-Chain-->>BTC: broadcast native txn
    BTC-->>User: funds arrive

    Note right of Z-Chain: if privacy, zSwapDeposit and zSwapRedeem flows.

4 RPC Mapping

Old REST RouteNew Chain RPC CallChain & Method
POST /swapsdex.swap.submit({txHex})X-Chain dex.swap.submit
GET /swaps/:iddex.swap.status({swapID})X-Chain dex.swap.status
POST /swaps/:id/sign(no-op, automated on T-Chain)
POST /swaps/:id/finalize(no-op, automated on SwapSig detection)

5 Validator & MPC Stack

X-Chain Node (luxd + DexFx)  <-->  dexfx plugin verifies SwapTx, watches proofs
T-Chain Node (luxd + mpckeyd) <-->  mpckeyd holds key shares, exposes gRPC sign_swap

6 State Diagrams

stateDiagram-v2
    [*] --> Pending
    Pending --> Signed: on MProof
    Signed  --> Final: on unlock + relay
    Pending --> Expired: expiry passed
    Signed  --> RevertRefund: on failure

7 Privacy Add‑On

Privacy flag triggers Z-Chain peg, shielded mint, and zSwapRedeem → zk-proof withdrawal.

8 Workflows & Tooling

Legacy Server CodeNew On-Chain Location
DB Swaps tabledex.swap.history indexer
Signature workermpckeyd daemon on T-Chain
REST routesJSON-RPC + WS dex.swap.*
Polling & webhooksWS push feeds and RPC responses

Rationale

This design fully decentralizes swap logic by moving book-keeping, MPC signing, and final settlement on-chain, removing trust in centralized servers and enabling light clients to participate via standard RPC and WebSocket feeds.

Backwards Compatibility

Swap features are gated behind the DexFx/SwapFx extension and RPC methods; nodes without the extension operate unchanged.

Security Considerations

  • Bound SwapSigTx relayer rights (multisig) to prevent unauthorized commits
  • Enforce expiry-based slashing on MPC nodes for liveness guarantees
  • Use gas and data limits on proofs to avoid DoS

Economic Impact (optional)

Operator MPC signers stake LUX and earn swap fees. Users pay gwei-priced fees in LUX for each swap transaction.

Open Questions (optional)

  1. Fee model for failed external transfers?
  2. Optimal expiry window vs on-chain gas cost?
  3. Depth of Z-Chain privacy integration?

MPC signers burn stake on misbehavior; relayer gas paid by fees built into SwapTx. Users pay gwei-priced fees in native LUX.

Implementation

On-Chain Swap Architecture

Location: ~/work/lux/node/vms/avm/ and ~/work/lux/node/vms/

T-Chain Signing: See LP-7319: T-Chain MPC Custody for MPC signing coordination.

SwapTx State Machine:

type SwapStatus uint8

const (
    PENDING SwapStatus = iota
    SIGNED
    FINAL
    EXPIRED
    FAILED
)

func (tx *SwapTx) Status() SwapStatus {
    // Check if SwapSigTx exists on T-Chain
    if mproof := getLightClientProof(tx.SwapID); mproof != nil {
        if isExpired(tx) {
            return EXPIRED
        }
        return FINAL
    }

    if time.Since(tx.CreatedAt) > SWAP_TIMEOUT {
        return EXPIRED
    }

    return PENDING
}

Testing:

cd ~/work/lux/node
go test ./vms/avm/plugins/swap/... -v
go test ./vms/mvm/... -v

# Load test with concurrent swaps
cd ~/work/lux/node
go test -run TestSwapLoadTest -timeout=5m ./vms/avm/plugins/swap/...

Swap RPC Endpoints

X-Chain dex.swap. Methods*:

  • dex.swap.submit({txHex}) - Submit SwapTx
  • dex.swap.status({swapID}) - Query swap status (PENDING/SIGNED/FINAL)
  • dex.swap.history({address,limit}) - Swap history for address

Performance SLAs:

  • Swap submission: <100 ms
  • Status query: <20 ms
  • MPC signing latency: <5 seconds (3-of-5 threshold)

Open Questions (optional)

  1. How to manage fee refund or insurance for failed external TXs?
  2. Optimal expiry windows vs on-chain gas costs?
  3. Z-Chain integration depth vs UX tradeoffs?

Test Cases

Unit Tests

  1. Order Validation

    • Test order structure validation
    • Verify price precision handling
    • Test quantity validation
  2. Matching Engine

    • Test price-time priority
    • Verify partial fill handling
    • Test order cancellation
  3. Settlement

    • Test atomic settlement
    • Verify balance updates
    • Test fee distribution

Integration Tests

  1. Order Flow

    • Test full order lifecycle
    • Verify matching correctness
    • Test concurrent order handling
  2. Cross-Asset Operations

    • Test multi-asset swaps
    • Verify exchange rate accuracy
    • Test slippage protection

Copyright and related rights waived via CC0.