> For the complete documentation index, see [llms.txt](https://docs.satsterminal.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.satsterminal.com/trade/swap-sdk/migration-guide.md).

# Migration Guide

{% hint style="success" %}
The SatsTerminal SDK V2 API introduces a significantly simplified approach to token swapping while maintaining full backward compatibility with V1. This guide will help you understand the conceptual differences and provide practical examples for migrating your code.
{% endhint %}

### Key Benefits of V2

{% stepper %}
{% step %}
**Simplified Direction Control**

Clear `fromToken`/`toToken` parameters instead of confusing `sell` boolean
{% endstep %}

{% step %}
**Automatic Order Management**

No need to manually handle orders - everything managed via `swapId`
{% endstep %}

{% step %}
**Multiple PSBT Support**

Can handle complex transactions requiring multiple PSBTs
{% endstep %}

{% step %}
**Normalized Data**

Consistent, clean response structures
{% endstep %}

{% step %}
**Better UX**

Automatic marketplace selection and optimization
{% endstep %}

{% step %}
**Reduced Complexity**

Fewer parameters and simpler responses
{% endstep %}
{% endstepper %}

#### Conceptual Differences <a href="#conceptual-differences" id="conceptual-differences"></a>

**1. Direction Control**

**V1 Problem**: The `sell` boolean parameter was confusing and error-prone.

```javascript
// V1: Confusing direction control
const quote = await client.fetchQuote({
  btcAmount: "0.00008",
  runeName: "GOLD•DUST",
  sell: false,  // What does this mean? Buy runes with BTC? Sell BTC for runes?
  address: "bc1..."
});
```

**V2 Solution**: Clear, intuitive `fromToken` and `toToken` parameters.

```javascript
// V2: Crystal clear direction
const quote = await client.swapQuote({
  amount: "0.00008",
  fromToken: "BTC",        // I'm swapping FROM BTC
  toToken: "GOLD DUST",    // I'm swapping TO GOLD DUST
  address: "bc1...",
  protocol: "alkanes",
  params: {}
});
```

**2. Order Management**

**V1 Problem**: Manual order selection and management across multiple API calls.

```javascript
// V1: Manual order management
const quote = await client.fetchQuote({ ... });
const selectedOrders = quote.selectedOrders;  

const psbt = await client.getPSBT({
  orders: selectedOrders,  // Must pass orders explicitly
  ...
});

const result = await client.confirmPSBT({
  orders: selectedOrders,  // Must pass orders again
  ...
});
```

**V2 Solution**: Automatic order management via `swapId`.

```javascript
// V2: Automatic order management
const quote = await client.swapQuote({ ... });
// Orders are automatically managed internally

const psbt = await client.swapPSBT({
  swapId: quote.swapId,  // No orders needed - all handled automatically
  ...
});

const result = await client.swapSubmit({
  swapId: psbt.swapId,   // Still no orders needed
  ...
});
```

**3. PSBT Handling**

**V1 Limitation**: Always returned a single PSBT.

```javascript
// V1: Single PSBT only
interface PSBTResponse {
  psbtBase64: string;    // Single PSBT
  psbtHex: string;       // Single PSBT
  inputs: number[];
}
```

**V2 Enhancement**: Can return multiple PSBTs for complex transactions.

```javascript
// V2: Multiple PSBTs supported
interface SwapV2PSBTResponse {
  psbts: SwapV2PSBT[];   // Array of PSBTs
  swapId: string;
}

interface SwapV2PSBT {
  base64: string;
  hex: string;
  inputs: number[];
}
```

**4. Data Structure Consistency**

**V1 Problem**: Inconsistent, nested data structures.

```javascript
// V1: Complex, inconsistent structure
{
  selectedOrders: [
    {
      market: "Unisat",           // Sometimes "market"
      marketplace: "MagicEden",   // Sometimes "marketplace"
      price: 0.00000004,          // Number format
      formattedUnitPrice: "0.490352", // String format
      // ... many other inconsistent fields
    }
  ],
  totalFormattedAmount: "0.00007542",
  totalPrice: "1843.84645",
  metrics: { /* complex nested structure */ }
}
```

**V2 Solution**: Normalized, consistent data format.

```javascript
// V2: Clean, consistent structure
{
  bestMarketplace: "IdClub",
  swapId: "st-mdyr4s5y-14277fae41249423",
  fromTokenAmount: "0.00007946",    // Consistent string format
  toTokenAmount: "1843.84645",      // Consistent string format
  metrics: {
    idclub: {
      percentFulfilled: "99.33",
      totalPurchased: "1843.84645000",
      percentDifference: "0.00",
      averageUnitPrice: "0.00000004"
    }
  },
  marketplaces: {
    IdClub: {
      fromTokenAmount: "0.00007946",
      toTokenAmount: "1843.84645",
      swapId: "st-mdyr4s5y-14277fae41249423"
    }
  }
}
```

#### Migration Examples <a href="#migration-examples" id="migration-examples"></a>

**Example 1: Basic Rune Purchase**

**V1 Implementation:**

```javascript
// V1: Complex rune purchase flow
async function buyRunesV1() {
  const client = new SatsTerminal({ apiKey: 'your-api-key' });
  
  try {
    // Step 1: Get quote
    const quote = await client.fetchQuote({
      btcAmount: 0.0001,
      runeName: "LOBO•THE•WOLF•PUP",
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      sell: false,  // Buying runes with BTC
      marketplaces: ["MagicEden"]
    });
    
    // Step 2: Generate PSBT with manual order management
    const psbtResponse = await client.getPSBT({
      orders: quote.selectedOrders,  // Manual order selection
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
      paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
      paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
      runeName: "LOBO•THE•WOLF•PUP",
      feeRate: 5,
      slippage: 9
    });
    
    // Step 3: Sign PSBT (user implementation)
    const signedPsbtBase64 = await signPSBT(psbtResponse.rbfProtected.base64);
    const signedRbfPsbtBase64 = await signPSBT(psbtResponse.rbfProtected.base64);
    
    // Step 4: Confirm with manual order management again
    const confirmation = await client.confirmPSBT({
      orders: quote.selectedOrders,  // Must pass orders again
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
      paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
      paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
      signedPsbtBase64: signedPsbtBase64,
      signedRbfPsbtBase64: signedRbfPsbtBase64,
      swapId: psbtResponse.swapId,
      runeName: "LOBO•THE•WOLF•PUP"
    });
    
    console.log('V1 Transaction completed:', confirmation.txid);
    return confirmation;
    
  } catch (error) {
    console.error('V1 Error:', error);
    throw error;
  }
}
```

**V2 Implementation (Migrated):**

```javascript
// V2: Simplified rune purchase flow
async function buyRunesV2() {
  const client = new SatsTerminal({ apiKey: 'your-api-key' });
  
  try {
    // Step 1: Get quote with clear direction
    const quote = await client.swapQuote({
      amount: "0.0001",
      fromToken: "BTC",                    // Clear: swapping FROM BTC
      toToken: "LOBO•THE•WOLF•PUP",        // Clear: swapping TO this rune
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      protocol: "runes",
      params: {},
      marketplaces: ["MagicEden"]
    });
    
    // Step 2: Generate PSBT - no manual order management needed
    const psbtResponse = await client.swapPSBT({
      marketplace: quote.bestMarketplace,  // Use best marketplace from quote
      swapId: quote.swapId,                // Automatic order management
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
      paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
      paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
      protocol: "runes",
      feeRate: 5,
      slippage: 9
    });
    
    // Step 3: Sign all PSBTs (V2 can have multiple)
    const signedPsbts = await Promise.all(
      psbtResponse.psbts.map(psbt => signPSBT(psbt.hex))
    );
    
    // Step 4: Submit - no order management needed
    const result = await client.swapSubmit({
      marketplace: quote.bestMarketplace,
      swapId: psbtResponse.swapId,         // Automatic order management
      address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
      publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
      paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
      paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
      protocol: "runes",
      signedPsbts: signedPsbts
    });
    
    console.log('V2 Transaction completed:', result.txid);
    return result;
    
  } catch (error) {
    console.error('V2 Error:', error);
    throw error;
  }
}
```

**Example 2: Selling Runes for BTC**

**V1 Implementation:**

```javascript
// V1: Selling runes (confusing direction)
async function sellRunesV1() {
  const client = new SatsTerminal({ apiKey: 'your-api-key' });
  
  const quote = await client.fetchQuote({
    btcAmount: "20000",  // Amount of runes to sell (confusing parameter name)
    runeName: "GOLD DUST", // Confusing parameter name
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    sell: true,  // This means we're selling runes for BTC
    marketplaces: ["Unisat"],
    protocol: 'alkanes'
  });
  
  const psbt = await client.getPSBT({
    orders: quote.selectedOrders,
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
    paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
    paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
    runeName: "GOLD DUST",
    sell: true,  // Must remember to set this again
    feeRate: 3
    protocol: 'alkanes'
  });
  
  const signedPsbt = await signPSBT(psbt.psbtHex);
  
  const result = await client.confirmPSBT({
    orders: quote.selectedOrders,
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
    paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
    paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
    signedPsbtBase64: signedPsbt,
    swapId: psbt.swapId,
    runeName: "GOLD DUST",
    sell: true  // Must remember this in every call
    protocol: 'alkanes'
  });
  
  return result;
}
```

**V2 Implementation (Migrated):**

```javascript
// V2: Selling runes (crystal clear direction)
async function sellRunesV2() {
  const client = new SatsTerminal({ apiKey: 'your-api-key' });
  
  const quote = await client.swapQuote({
    amount: "20000",
    fromToken: "GOLD DUST",  // Clear: swapping FROM GOLD DUST (protocol agnostic)
    toToken: "BTC",          // Clear: swapping TO BTC
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    protocol: "alkanes",
    params: {}
  });
  
  const psbt = await client.swapPSBT({
    marketplace: quote.bestMarketplace,
    swapId: quote.swapId,    // No need to track orders
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
    paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559wzk4epc",
    paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
    protocol: "alkanes",
    feeRate: 3,
    slippage: 5
  });
  
  const signedPsbts = await Promise.all(
    psbt.psbts.map(p => signPSBT(p.hex))
  );
  
  const result = await client.swapSubmit({
    marketplace: quote.bestMarketplace,
    swapId: psbt.swapId,     // No need to track orders
    address: "bc1p4mffk7l9a040dgqrl8spunwkguxn68ldwx848urafar9sj85nnrq9rcvyj",
    publicKey: "03962e74f53d3620f82aab9ecfadbaa2d2b34ab1d794b346f53deb4c1a9d287bfc",
    paymentAddress: "bc1qjtyqu4uqrpnvsfg9tq0wzzsdge3e559vzk4epc",
    paymentPublicKey: "02325fb34ee9ff76df92b34d13059fa8d14008a9426fb1de52f038bfdca5075588",
    protocol: "alkanes",
    signedPsbts: signedPsbts
  });
  
  return result;
}
```

#### Migration Checklist <a href="#migration-checklist" id="migration-checklist"></a>

{% stepper %}
{% step %}
**Update Method Names**

* Replace `fetchQuote()` with `swapQuote()`
* Replace `getPSBT()` with `swapPSBT()`
* Replace `confirmPSBT()` with `swapSubmit()`
  {% endstep %}

{% step %}
**Update Parameters**

* Replace `sell: boolean` with `fromToken` and `toToken` strings
* Replace `btcAmount` with `amount` (works for any token)
* Replace `runeName` with appropriate `fromToken` or `toToken`
* Add `protocol` parameter ("runes" or "alkanes")
  {% endstep %}

{% step %}
**Update Response Handling**

* Replace `quote.selectedOrders` usage with `quote.swapId`
* Update PSBT handling to support multiple PSBTs: `psbt.psbts[]`
* Remove manual order management code
* Update data access patterns for normalized structure
  {% endstep %}

{% step %}
**Update PSBT Signing**

* Change from single PSBT to array of PSBTs
* Update signing loop: `psbt.psbts.map(p => signPSBT(p.hex))` or use built-in wallet signPsbts method.
* Change parameter from `signedPsbtBase64` to `signedPsbts[]`
  {% endstep %}

{% step %}
**Simplify Error Handling**

* Remove order-related error handling
* Simplify state management error handling
* Focus on swap-specific errors (slippage, balance, etc.)
  {% endstep %}
  {% endstepper %}

#### Common Pitfalls and Solutions <a href="#common-pitfalls-and-solutions" id="common-pitfalls-and-solutions"></a>

**Pitfall 1: Single PSBT Assumption**

**Problem**: Assuming V2 returns only one PSBT like V1.

**Solution**:

```javascript
// V1: Single PSBT
const signedPsbt = await signPSBT(psbt.psbtHex);

// V2: Multiple PSBTs
const signedPsbts = await Promise.all(
  psbt.psbts.map(p => signPSBT(p.hex)) // or signPSBTs for some wallets
);
```

**Pitfall 2: Order Management Confusion**

**Problem**: Trying to manage orders manually in V2.

**Solution**:

```javascript
// V1: Manual order management
const psbt = await client.getPSBT({
  orders: quote.selectedOrders,  // Don't do this in V2
  // ...
});

// V2: Automatic order management
const psbt = await client.swapPSBT({
  swapId: quote.swapId,  // Use swapId instead
  // ...
});
```

**Pitfall 3: Direction Confusion**

**Problem**: Converting `sell` boolean incorrectly.

**Solution**:

```javascript
// V1: Confusing sell parameter
const quote = await client.fetchQuote({
  btcAmount: "0.0001",
  runeName: "GOLD DUST",
  sell: false  // Buying runes with BTC
});

// V2: Clear direction - CORRECT conversion
const quote = await client.swapQuote({
  amount: "0.0001",
  fromToken: "BTC",        // What we're spending
  toToken: "GOLD DUST",    // What we're getting
  // ...
});

// WRONG conversion would be:
// fromToken: "GOLD DUST", toToken: "BTC" (this would be selling)
```
