Native Swap Integration on T-Chain, X-Chain, and Z-Chain
Deep-integration blueprint to migrate legacy swaps REST API (swaps.ts) into fully on-chain M/X/Z chain transactions and RPC
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:
| Method | Path | Purpose |
|---|---|---|
| POST /swaps | create a cross-chain swap | record DB row, key-manager signs |
| GET /swaps/:id | poll swap status | PENDING / SIGNED / FINAL |
| POST /swaps/:id/sign | privileged broadcast MPC signature | |
| POST /swaps/:id/finalize | mark COMPLETED, emit webhook |
2 High-Level Chain Roles
| Chain | Role for swaps |
|---|---|
| X-Chain | SwapTx locks escrow funds; emits SwapID = txID; user intent |
| T-Chain | SwapSigTx 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)
| Method | Path | Purpose |
|---|---|---|
| POST /swaps | create a cross-chain swap | record DB row, key-manager signs |
| GET /swaps/:id | poll swap status | PENDING / SIGNED / FINAL |
| POST /swaps/:id/sign | privileged broadcast MPC signature | |
| POST /swaps/:id/finalize | mark COMPLETED, emit webhook |
Pain points:
- Swap book in Postgres → trusted central server
- Signature collection off-chain in Node.js worker
- Finality tracking via polling and cron jobs
Goal: Move this workflow entirely on-chain so front-ends remain stateless.
1 High-Level Chain Roles
| Chain | Role for swaps |
|---|---|
| X-Chain | SwapTx locks escrow funds; emits SwapID = txID; user intent |
| T-Chain | SwapSigTx 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 Route | New Chain RPC Call | Chain & Method |
|---|---|---|
| POST /swaps | dex.swap.submit({txHex}) | X-Chain dex.swap.submit |
| GET /swaps/:id | dex.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 Code | New On-Chain Location |
|---|---|
| DB Swaps table | dex.swap.history indexer |
| Signature worker | mpckeyd daemon on T-Chain |
| REST routes | JSON-RPC + WS dex.swap.* |
| Polling & webhooks | WS 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
SwapSigTxrelayer 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)
- Fee model for failed external transfers?
- Optimal expiry window vs on-chain gas cost?
- 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 SwapTxdex.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)
- How to manage fee refund or insurance for failed external TXs?
- Optimal expiry windows vs on-chain gas costs?
- Z-Chain integration depth vs UX tradeoffs?
Test Cases
Unit Tests
-
Order Validation
- Test order structure validation
- Verify price precision handling
- Test quantity validation
-
Matching Engine
- Test price-time priority
- Verify partial fill handling
- Test order cancellation
-
Settlement
- Test atomic settlement
- Verify balance updates
- Test fee distribution
Integration Tests
-
Order Flow
- Test full order lifecycle
- Verify matching correctness
- Test concurrent order handling
-
Cross-Asset Operations
- Test multi-asset swaps
- Verify exchange rate accuracy
- Test slippage protection
Copyright
Copyright and related rights waived via CC0.