Cross-Chain Swap
The SWAP action lets you trade a token on one chain for a token on a different chain — for example, RAREPEPE on Bitcoin for LTCTOKEN on Litecoin — without a centralized exchange. The xchain-hub coordinates matching across chains.
Important: SWAP works only between XChain tokens. It does not support native coins (BTC, LTC, DOGE) directly. To sell a token for native coin, use a DISPENSER instead.
How It Works
- Alice creates a SWAP offer on the Bitcoin chain, escrowing
GIVE_AMOUNTof her token. - The hub sees the open swap and broadcasts it to Litecoin indexers.
- Bob finds the swap and creates a matching SWAP on the Litecoin chain, escrowing his
GET_AMOUNT. - The hub detects both sides and signals settlement.
- Each indexer credits the counterparty: Alice receives
GET_AMOUNTon Litecoin; Bob receivesGIVE_AMOUNTon Bitcoin. - If no match occurs before expiration, tokens are returned automatically.
Step 1: Create the Swap Offer (Chain A — Bitcoin)
Alice wants to trade 1 RAREPEPE (on BTC) for 1000 LTCTOKEN (on LTC).
const XChainSDK = require('xchain-sdk');
// Alice's SDK, pointed at the Bitcoin regtest stack
const aliceSdk = new XChainSDK({ hubUrl: 'http://localhost:35500' });
const swapAction = aliceSdk.swap({
giveCoin: 'BTC',
giveTick: 'RAREPEPE',
giveAmount: '1',
getCoin: 'LTC',
getTick: 'LTCTOKEN',
getAmount: '1000',
getAddress: 'ltc1qalicesltcaddress...', // Alice's LTC address to receive tokens
// expiration: Math.floor(Date.now() / 1000) + 86400 * 3, // 3-day window, optional
});
// Returns: "SWAP|0|BTC|RAREPEPE|1|LTC|LTCTOKEN|1000|ltc1qalicesltcaddress...|..."
const psbt = await aliceSdk.encoder.createPSBT({
action: swapAction,
publicKey: 'ALICE_BTC_PUBLIC_KEY',
utxos: aliceBtcUtxos,
});
const txid = await signAndBroadcast(psbt.psbt);
await mineBlock(); // mine on Bitcoin regtest
// Get the ACTION_INDEX for Bob to reference
const swapActions = await aliceSdk.explorer.getActions({ txid });
const swapActionIndex = swapActions[0].action_index;
console.log('Swap ACTION_INDEX:', swapActionIndex);
Alice’s RAREPEPE is now escrowed. The hub becomes aware of this open swap.
Step 2: Bob Discovers the Swap
Bob’s application queries the hub or explorer for open swaps that match his interests.
// Bob's SDK, pointed at the Litecoin stack
const bobSdk = new XChainSDK({ hubUrl: 'http://localhost:35500' });
// Query open swaps wanting LTCTOKEN
const openSwaps = await aliceSdk.explorer.getActions({
tick: 'RAREPEPE',
// filter by action type on client
});
const swaps = openSwaps.filter(a => a.action === 'SWAP' && a.status === 'open');
console.log('Open swaps:', swaps);
// [{
// action_index: 1234,
// give_tick: 'RAREPEPE',
// give_amount: '1',
// get_coin: 'LTC',
// get_tick: 'LTCTOKEN',
// get_amount: '1000',
// get_address: 'ltc1qalicesltcaddress...',
// status: 'open',
// expiration: 1767254400,
// ...
// }]
Step 3: Bob Matches the Swap (Chain B — Litecoin)
Bob creates a matching SWAP on Litecoin, referencing Alice’s offer by its ACTION_INDEX.
// Bob matches Alice's swap
const matchAction = bobSdk.swap({
giveCoin: 'LTC',
giveTick: 'LTCTOKEN',
giveAmount: '1000',
getCoin: 'BTC',
getTick: 'RAREPEPE',
getAmount: '1',
getAddress: 'bc1qbobsbtcaddress...', // Bob's BTC address to receive RAREPEPE
// No expiration needed — matching is immediate
});
const matchPsbt = await bobSdk.encoder.createPSBT({
action: matchAction,
publicKey: 'BOB_LTC_PUBLIC_KEY',
utxos: bobLtcUtxos,
});
const matchTxid = await signAndBroadcast(matchPsbt.psbt);
await mineLtcBlock();
Bob’s LTCTOKEN is now escrowed on Litecoin.
Step 4: Settlement
The hub detects that both sides of the swap are in their respective confirmed blocks. It signals both indexers to settle:
- The Bitcoin indexer credits
bc1qbobsbtcaddress...with 1RAREPEPE. - The Litecoin indexer credits
ltc1qalicesltcaddress...with 1000LTCTOKEN.
Both escrows are released and the swap closes automatically. No further action is required from either party.
// Verify Alice received LTCTOKEN on LTC
const aliceLtcBalances = await bobSdk.explorer.getBalances({
address: 'ltc1qalicesltcaddress...',
});
console.log('Alice LTC balances:', aliceLtcBalances);
// Verify Bob received RAREPEPE on BTC
const bobBtcBalances = await aliceSdk.explorer.getBalances({
address: 'bc1qbobsbtcaddress...',
});
console.log('Bob BTC balances:', bobBtcBalances);
Step 5: Cancelling or Editing a Swap
If no match arrives, Alice can cancel to recover her escrowed tokens:
const cancelAction = aliceSdk.swap({
version: 1,
swapActionIndex: swapActionIndex,
memo: 'No takers, cancelling',
});
const cancelPsbt = await aliceSdk.encoder.createPSBT({
action: cancelAction,
publicKey: 'ALICE_BTC_PUBLIC_KEY',
utxos: aliceBtcUtxos,
});
await signAndBroadcast(cancelPsbt.psbt);
await mineBlock();
To extend the swap window:
const editAction = aliceSdk.swap({
version: 2,
swapActionIndex: swapActionIndex,
expiration: Math.floor(Date.now() / 1000) + 86400 * 7, // 7 more days
});
Rules and Constraints
- SWAP does not move native coin — only XChain tokens.
GET_ADDRESSmust be a valid address onGET_COIN’s network. IfGET_COINis the same network as the swap transaction,GET_ADDRESScan be omitted and theSOURCEaddress is used.- Swap expiration is a Unix timestamp, not a block height.
- An
ALLOW_LISTorBLOCK_LISTcan restrict which counterparties can match your swap. - The hub must be running and connected to both chains for cross-chain coordination to work.
Same-Chain Swap
If both tokens are on the same chain, an ORDER is simpler and does not require hub coordination. See Integration_Patterns.md for DEX patterns.
Next Steps
- Query_The_Explorer.md — discover and monitor open swaps
- Integration_Patterns.md — building a DEX frontend
- Advanced_Token_Features.md — allow/block lists for swaps
Copyright © 2025–2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.