# Quickstart

Go from zero to a working swap integration in under 5 minutes. This guide covers quoting, connecting to the relayer, submitting a relayed transaction, and waiting for confirmation — all in one runnable example.

***

## Prerequisites

* A MultiversX wallet with an Ed25519 keypair
* Enough EGLD in the wallet to cover gas fees (typically 0.05 EGLD for a swap)
* Node.js 18+ with `npm` available

***

## Step 1: Get a swap quote

The arb-algo API finds the optimal route across all supported DEXs and returns a ready-to-use transaction payload.

```bash
curl "https://swap.xoxno.com/api/v1/quote?\
from=WEGLD-bd4d79&\
to=USDC-c76f1f&\
amountIn=1000000000000000000&\
slippage=0.01"
```

The response includes `txData` (base64 smart contract call payload), `amountOut` (expected output), and `amountOutMin` (minimum output after slippage). You use all three when building the transaction.

```json
{
  "from": "WEGLD-bd4d79",
  "to": "USDC-c76f1f",
  "amountIn": "1000000000000000000",
  "amountOut": "42500000",
  "amountOutMin": "42075000",
  "priceImpact": 0.0012,
  "txData": "c3dhcEV4YWN0QW1...",
  ...
}
```

{% hint style="info" %}
Amounts are always in the smallest unit of each token. WEGLD has 18 decimals (`1000000000000000000` = 1 WEGLD). USDC has 6 decimals (`42500000` = 42.5 USDC).
{% endhint %}

***

## Step 2: Connect to the relayer and subscribe to gas stats

The relayer WebSocket streams per-shard gas statistics every 50ms. Subscribe before building the transaction to set a competitive `gasPrice`.

```javascript
const ws = new WebSocket('wss://relayer.xoxno.com/ws');

await new Promise((resolve, reject) => {
  ws.onopen = resolve;
  ws.onerror = () => reject(new Error('WebSocket connection failed'));
});

// Subscribe to gas stats
ws.send(JSON.stringify({ action: 'subscribe', topic: 'gasStats' }));

// Wait for the first update
const gasStats = await new Promise((resolve) => {
  const stats = new Map();
  ws.addEventListener('message', function h(e) {
    const msg = JSON.parse(e.data);
    if (msg.type === 'gasStats') {
      stats.set(msg.data.shard, msg.data);
      ws.removeEventListener('message', h);
      resolve(stats);
    }
  });
});
```

***

## Step 3: Subscribe to tx-status before sending

Compute the transaction hash from the fully signed transaction and subscribe to `tx-status/{hash}` before sending the relay action. The event fires once and is never replayed — subscribing after the fact means missing it.

```javascript
// After building and signing the transaction (see full example below):
const txHash = computeTxHash(signedTx); // from @multiversx/sdk-core

ws.send(JSON.stringify({ action: 'subscribe', topic: `tx-status/${txHash}` }));

const confirmation = new Promise((resolve, reject) => {
  const ttl = setTimeout(() => reject(new Error('No confirmation within 60s')), 60_000);
  ws.addEventListener('message', function h(e) {
    const msg = JSON.parse(e.data);
    if (msg.type === 'tx-status' && msg.data?.hash === txHash) {
      clearTimeout(ttl);
      ws.removeEventListener('message', h);
      resolve(msg.data);
    }
  });
});
```

***

## Step 4: Send the relay transaction

Send a `relay` action containing your signed transaction. The relayer validates shard alignment, appends its co-signature, and broadcasts to the MultiversX P2P network.

```javascript
const RELAYER_ADDRESSES = {
  0: 'erd1l0x0n5yxsfcy93gm0vyvx9m9f7cte9h9vuq4am33ugpw3d5r3hvqx6f59h',
  1: 'erd12yxd5phejzw83gn8qh6jfz6q9a0ekyyhkfd3c49r03mxw25l3a5swq3nf7',
  2: 'erd13jxp0yjh7gjvzgrg5mj7e8rzhn5lzcye45l0p6e5996d543r7vrq9e50za'
};

ws.send(JSON.stringify({
  action: 'relay',
  requestId: 'my-swap-1',
  tx: signedTx    // includes signature and relayer field
}));
```

Then await the relay acknowledgment (broadcast confirmed) and the tx-status event (on-chain confirmed separately):

```javascript
const ack = await new Promise((resolve, reject) => {
  const timeout = setTimeout(() => reject(new Error('Relay ack timeout')), 15_000);
  ws.addEventListener('message', function h(e) {
    const msg = JSON.parse(e.data);
    if (msg.action === 'relay' && msg.requestId === 'my-swap-1') {
      clearTimeout(timeout);
      ws.removeEventListener('message', h);
      if (msg.status === 'failed') reject(new Error(msg.failed?.[0]?.reason));
      else resolve(msg);
    }
  });
});

console.log('Broadcast hash:', ack.hashes[0]);

const result = await confirmation;
console.log('On-chain status:', result.status); // 'success' or 'fail'
```

***

## Complete Example

The following self-contained script performs a WEGLD → USDC swap using the arb-algo quote API and the relayer WebSocket. Install dependencies, add your wallet, and run it.

```javascript
import bech32 from 'bech32';
// npm install bech32 @multiversx/sdk-core @multiversx/sdk-wallet

const AGGREGATOR_CONTRACT = 'erd1qqqqqqqqqqqqqpgqmsgtu8888h0j3nkxvp5kkeydl7hxs00dsq2snk2f5t';
const RELAYER_ADDRESSES = {
  0: 'erd1l0x0n5yxsfcy93gm0vyvx9m9f7cte9h9vuq4am33ugpw3d5r3hvqx6f59h',
  1: 'erd12yxd5phejzw83gn8qh6jfz6q9a0ekyyhkfd3c49r03mxw25l3a5swq3nf7',
  2: 'erd13jxp0yjh7gjvzgrg5mj7e8rzhn5lzcye45l0p6e5996d543r7vrq9e50za'
};

// --- Configure your wallet ---
const SENDER_ADDRESS = 'erd1your_address_here';
const ACCOUNT_NONCE = 42; // fetch from https://api.multiversx.com/accounts/{address}

function getSenderShard(address) {
  const decoded = bech32.decode(address);
  const bytes = bech32.fromWords(decoded.words);
  return bytes[bytes.length - 1] % 3;
}

async function main() {
  // 1. Fetch quote
  const params = new URLSearchParams({
    from: 'WEGLD-bd4d79',
    to: 'USDC-c76f1f',
    amountIn: '1000000000000000000', // 1 WEGLD
    slippage: '0.01'
  });
  const quoteRes = await fetch(`https://swap.xoxno.com/api/v1/quote?${params}`);
  if (!quoteRes.ok) throw new Error(`Quote failed: ${(await quoteRes.json()).error}`);
  const quote = await quoteRes.json();
  console.log(`Quote: ${quote.amountOutShort} USDC for 1 WEGLD (impact: ${(quote.priceImpact * 100).toFixed(3)}%)`);

  // 2. Connect WebSocket
  const ws = await new Promise((resolve, reject) => {
    const sock = new WebSocket('wss://relayer.xoxno.com/ws');
    sock.onopen = () => resolve(sock);
    sock.onerror = () => reject(new Error('WebSocket failed'));
  });

  // 3. Subscribe to gas stats, wait for first update
  const gasStats = await new Promise((resolve) => {
    const stats = new Map();
    ws.addEventListener('message', function h(e) {
      const msg = JSON.parse(e.data);
      if (msg.type === 'gasStats') {
        stats.set(msg.data.shard, msg.data);
        ws.removeEventListener('message', h);
        resolve(stats);
      }
    });
    ws.send(JSON.stringify({ action: 'subscribe', topic: 'gasStats' }));
  });

  // 4. Build transaction
  const shard = getSenderShard(SENDER_ADDRESS);
  const relayerAddress = RELAYER_ADDRESSES[shard];
  const shardData = gasStats.get(shard);
  const gasPrice = shardData ? shardData.gasPrice.percentiles.p75 : 1_000_000_000;

  const tx = {
    nonce: ACCOUNT_NONCE,
    value: '0',
    receiver: AGGREGATOR_CONTRACT,
    sender: SENDER_ADDRESS,
    gasPrice,
    gasLimit: 50_000_000,
    data: quote.txData,   // base64 payload from arb-algo
    chainID: '1',
    version: 2,
    relayer: relayerAddress  // set BEFORE signing
  };

  // Sign the transaction — replace with your actual signing logic
  // tx.signature = await yourWalletSigner.sign(serializeForSigning(tx));
  tx.signature = 'YOUR_SIGNATURE_HEX';

  // 5. Subscribe to tx-status BEFORE sending
  const txHash = 'COMPUTED_FROM_SIGNED_TX'; // use computeTxHash(tx) from sdk-core
  ws.send(JSON.stringify({ action: 'subscribe', topic: `tx-status/${txHash}` }));

  const confirmation = new Promise((resolve, reject) => {
    const ttl = setTimeout(() => reject(new Error('No confirmation within 60s')), 60_000);
    ws.addEventListener('message', function h(e) {
      const msg = JSON.parse(e.data);
      if (msg.type === 'tx-status' && msg.data?.hash === txHash) {
        clearTimeout(ttl);
        ws.removeEventListener('message', h);
        resolve(msg.data);
      }
    });
  });

  // 6. Send relay action
  const requestId = `swap-${Date.now()}`;
  await new Promise((resolve, reject) => {
    const timeout = setTimeout(() => reject(new Error('Relay ack timeout')), 15_000);
    ws.addEventListener('message', function h(e) {
      const msg = JSON.parse(e.data);
      if (msg.action === 'relay' && msg.requestId === requestId) {
        clearTimeout(timeout);
        ws.removeEventListener('message', h);
        if (msg.status === 'failed') reject(new Error(msg.failed?.[0]?.reason));
        else resolve(msg);
      }
    });
    ws.send(JSON.stringify({ action: 'relay', requestId, tx }));
  });

  console.log('Transaction broadcast:', txHash);

  // 7. Wait for on-chain confirmation
  const result = await confirmation;
  ws.close();

  if (result.status === 'success') {
    console.log('Swap successful.');
  } else {
    console.error('Swap failed on-chain:', result.reason);
  }
}

main().catch(console.error);
```

***

## What to Do Next

* [Transaction Lifecycle](https://docs.xoxno.com/developers/guides/transaction-lifecycle) — Detailed error handling for every step
* [Gas Optimization](https://docs.xoxno.com/developers/guides/gas-optimization) — Adaptive gas pricing using mempool pressure
* [Quote Endpoint](https://docs.xoxno.com/developers/aggregator-api/quote) — All request parameters and response fields
* [Transaction Relaying](https://docs.xoxno.com/developers/relayer-api/transaction-relaying) — Relay action reference, batch relay
* [Account Subscriptions](https://docs.xoxno.com/developers/relayer-api/account-subscriptions) — Subscribe to real-time balance updates
