npm package: @saturnbtcio/pools-api-sdk
Description: SDK Library for interacting with Saturn Liquidity Pools API
Author: Saturn BTC
Latest version: 0.0.4

# Pools SDK Documentation

Welcome to the **Pools SDK** documentation!

---

## Table of Contents

- [Introduction](#introduction)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
  - [Initialize Pool](#initialize-pool)
  - [Add Pool Shards](#add-pool-shards)
  - [Open Position](#open-position)
  - [Increase Liquidity](#increase-liquidity)
  - [Decrease Liquidity](#decrease-liquidity)
  - [Swap Tokens](#swap-tokens)
  - [Get Swap Details](#get-swap-details)
- [Error Handling](#error-handling)
- [Advanced Topics](#advanced-topics)
  - [Applying Slippage](#applying-slippage)
  - [Handling PSBTs](#handling-psbts)
- [Conclusion](#conclusion)

---

## Introduction

The Pools SDK provides a convenient way to interact with the Pools API, which includes endpoints for managing liquidity pools, performing token swaps, and more. The SDK abstracts away the complexities of HTTP requests and data serialization, allowing you to focus on building your application logic.

## Installation

To install the SDK, run the following command:

```bash
npm install @saturnbtcio/pools-api-sdk
```

## Configuration

Before using the SDK, you need to configure it with the base URL of your Saturn API server. Most endpoints require authentication using an API key, so ensure that you include the `x-api-key` header in the configuration.

```typescript
import { IConnection } from "@saturnbtcio/pools-api-sdk";

const connection: IConnection = {
  host: "https://api-dev.saturnbtc.io", // Replace with your API base URL
  headers: {
    "x-api-key": "your-api-key", // Replace with your actual API key
  },
};
```

You can pass this `connection` object to the SDK functions when making API calls.

## Usage

Below are examples of how to use the SDK to interact with various endpoints.

### Initialize Pool

#### Step 1: Generate PSBT

```typescript
import sdk from "@saturnbtcio/pools-api-sdk";

const psbtResponse = await sdk.functional.v0.pool.initialize_pool.psbt.initializePoolPsbt(
  {
    pubkey: "your-public-key",
    feeRate: 10, // Fee rate in satoshis per byte
    accountsLength: 11, // Number of accounts to create
  },
  connection
);

if (!psbtResponse.success) {
  if (
    psbtResponse.status === 401 ||
    psbtResponse.status === 403 ||
    psbtResponse.status === 500
  ) {
    // Handle common errors
    // 401: Unauthorized
    // 403: User not found
    // 500: Internal server error
  }

  // Handle PoolErrorException
  console.error("Error:", psbtResponse.data);
  throw new Error("Failed to generate PSBT");
}

// Access PSBT data
const { psbt, fee, utxosToSign, accounts } = psbtResponse.data;
```

- **Parameters:**
  - `runePubkey`: Your wallet's Taproot pubkey.
  - `paymentPubkey`: (Optional) Your wallet's payment pubkey (for xverse and others).
  - `feeRate`: Desired fee rate for the transaction.
  - `accountsLength`: Number of accounts to create for the pool.
  - `connection`: The connection object configured with your API host and headers.

#### Step 2: Sign PSBT and Generate Message

Sign the PSBT using your wallet, then generate the message hash.

```typescript
// Implement signPsbt according to your wallet
const signedPsbt = signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign);

const messageHashResponse = await sdk.functional.v0.pool.initialize_pool.message.initializePoolMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    accounts: psbtResponse.data.accounts,
    feeRate: 10,
    signedPsbt,
    token0: "token0-id",
    token1: "token1-id",
    feeTier: 3000, // Example fee tier in basis points (e.g., 0.3% fee tier is 3000)
  },
  connection
);

if (!messageHashResponse.success) {
  // Handle error
  console.error("Error:", messageHashResponse.data);
  throw new Error("Failed to generate message hash");
}

const messageHash = messageHashResponse.data;
```

- **Parameters:**
  - `accounts`: Accounts from the PSBT response.
  - `signedPsbt`: Your signed PSBT in base64 format.
  - `token0`, `token1`: Identifiers of the tokens in the pool.
  - `feeTier`: The fee tier for the pool in basis points.
  - `connection`: The connection object.

#### Step 3: Submit Signed Message

Sign the message hash using BIP322 with your Taproot address, then submit the final request.

```typescript
// Implement signMessage according to your wallet
const signature = signMessage(messageHash);

const initializePoolResponse = await sdk.functional.v0.pool.initialize_pool.initializePool(
  {
    pubkey: "your-public-key",
    accounts: psbtResponse.data.accounts,
    feeRate: 10,
    signedPsbt,
    token0: "token0-id",
    token1: "token1-id",
    feeTier: 3000,
    signature,
  },
  connection
);

if (!initializePoolResponse.success) {
  // Handle error
  console.error("Error:", initializePoolResponse.data);
  throw new Error("Failed to initialize pool");
}

const transactionId = initializePoolResponse.data;
console.log("Pool initialized with transaction ID:", transactionId);
```

- **Parameters:**
  - `signature`: Your signature of the message hash.
  - `connection`: The connection object.

### Add Pool Shards

Follow similar steps as **Initialize Pool**, but use the `addPoolShards` methods.

#### Step 1: Generate PSBT

```typescript
const psbtResponse = await sdk.functional.v0.pool.add_pool_shards.psbt.addPoolShardsPsbt(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    feeRate: 10,
    accountsLength: 2,
  },
  connection
);

// Handle errors as shown previously
```

#### Step 2: Sign PSBT and Generate Message

```typescript
const signedPsbt = signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign);

const messageHashResponse = await sdk.functional.v0.pool.add_pool_shards.message.addPoolShardsMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    accounts: psbtResponse.data.accounts,
    feeRate: 10,
    signedPsbt,
  },
  connection
);

// Handle errors as shown previously
```

#### Step 3: Submit Signed Message

```typescript
const signature = signMessage(messageHashResponse.data);

const addShardsResponse = await sdk.functional.v0.pool.add_pool_shards.addPoolShards(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    accounts: psbtResponse.data.accounts,
    feeRate: 10,
    signedPsbt,
    signature,
  },
  connection
);

// Handle errors as shown previously

console.log("Added pool shards with transaction ID:", addShardsResponse.data);
```

### Open Position

#### Step 1: Generate PSBT

```typescript
const psbtResponse = await sdk.functional.v0.pool.open_position.psbt.openPositionPsbt(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    token0: "token0-id",
    token1: "token1-id",
    amount0: "100000000", // Amount in smallest units (e.g., satoshis)
    amount1: "50000000",
    initializeAccountUtxo: true,
    feeRate: 10,
  },
  connection
);

// Handle errors as shown previously
```

#### Step 2: Sign PSBT and Generate Message

```typescript
const signedPsbt = signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign);

const messageHashResponse = await sdk.functional.v0.pool.open_position.message.openPositionMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amount0: "100000000",
    amount1: "50000000",
    feeRate: 10,
    signedPsbt,
    account: psbtResponse.data.account, // From PSBT response
  },
  connection
);

// Handle errors as shown previously
```

#### Step 3: Submit Signed Message

```typescript
const signature = signMessage(messageHashResponse.data);

const openPositionResponse = await sdk.functional.v0.pool.open_position.openPosition(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amount0: "100000000",
    amount1: "50000000",
    feeRate: 10,
    signedPsbt,
    account: psbtResponse.data.account,
    signature,
  },
  connection
);

// Handle errors as shown previously

console.log("Position opened with transaction ID:", openPositionResponse.data);
```

### Increase Liquidity

#### Step 1: Generate PSBT

```typescript
const psbtResponse = await sdk.functional.v0.pool.increase_liquidity.psbt.increaseLiquidityPsbt(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    token0: "token0-id",
    token1: "token1-id",
    amount0: "50000000",
    amount1: "25000000",
    initializeAccountUtxo: false,
    feeRate: 10,
  },
  connection
);

// Handle errors as shown previously
```

#### Step 2: Sign PSBT and Generate Message

```typescript
const signedPsbt = signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign);

const messageHashResponse = await sdk.functional.v0.pool.increase_liquidity.message.increaseLiquidityMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amount0: "50000000",
    amount1: "25000000",
    feeRate: 10,
    signedPsbt,
    positionPubKey: "position-public-key",
  },
  connection
);

// Handle errors as shown previously
```

#### Step 3: Submit Signed Message

```typescript
const signature = signMessage(messageHashResponse.data);

const increaseLiquidityResponse = await sdk.functional.v0.pool.increase_liquidity.increaseLiquidity(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amount0: "50000000",
    amount1: "25000000",
    feeRate: 10,
    signedPsbt,
    positionPubKey: "position-public-key",
    signature,
  },
  connection
);

// Handle errors as shown previously

console.log("Liquidity increased with transaction ID:", increaseLiquidityResponse.data);
```

### Decrease Liquidity

#### Optional Step: Generate PSBT

Creating a PSBT is optional for decreasing liquidity. If you need to cover network fees, generate a PSBT.

```typescript
const psbtResponse = await sdk.functional.v0.pool.decrease_liquidity.psbt.decreaseLiquidityPsbt(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    feeRate: 10,
  },
  connection
);

// Handle errors as shown previously

const signedPsbt = psbtResponse.success
  ? signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign)
  : undefined;
```

#### Step 1: Generate Message

```typescript
const messageHashResponse = await sdk.functional.v0.pool.decrease_liquidity.message.decreaseLiquidityMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    positionPubKey: "position-public-key",
    liquidityAmount: "50000000",
    feeRate: 10,
    minToken0: "49000000", // Minimum expected amounts
    minToken1: "24000000",
    withdrawAddressToken0: "btc-address",
    withdrawAddressToken1: "token-address",
    signedPsbt, // Include if generated
  },
  connection
);

// Handle errors as shown previously
```

#### Step 2: Submit Signed Message

```typescript
const signature = signMessage(messageHashResponse.data);

const decreaseLiquidityResponse = await sdk.functional.v0.pool.decrease_liquidity.decreaseLiquidity(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    positionPubKey: "position-public-key",
    liquidityAmount: "50000000",
    feeRate: 10,
    minToken0: "49000000",
    minToken1: "24000000",
    withdrawAddressToken0: "btc-address",
    withdrawAddressToken1: "token-address",
    signedPsbt, // Include if generated
    signature,
  },
  connection
);

// Handle errors as shown previously

console.log("Liquidity decreased with transaction ID:", decreaseLiquidityResponse.data);
```

### Swap Tokens

#### Step 1: Generate PSBT

```typescript
const psbtResponse = await sdk.functional.v0.pool.swap.psbt.swapPsbt(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amountIn: "100000000",
    amountOut: "95000000",
    feeRate: 10,
    zeroToOne: true,
    exactIn: true,
  },
  connection
);

// Handle errors as shown previously

const signedPsbt = signPsbt(psbtResponse.data.psbt, psbtResponse.data.utxosToSign);

const signedSplitRunePsbt = psbtResponse.data.splitRunePsbt
  ? signPsbt(psbtResponse.data.splitRunePsbt.psbt, psbtResponse.data.splitRunePsbt.utxosToSign)
  : undefined;
```

#### Step 2: Generate Message

```typescript
const messageHashResponse = await sdk.functional.v0.pool.swap.message.swapMessage(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amountIn: "100000000",
    amountOut: "95000000",
    feeRate: 10,
    zeroToOne: true,
    exactIn: true,
    signedPsbt,
    splitRunePsbt: signedSplitRunePsbt,
    shardPubkeys: psbtResponse.data.shardPubkeys,
  },
  connection
);

// Handle errors as shown previously
```

#### Step 3: Submit Signed Message

```typescript
const signature = signMessage(messageHashResponse.data);

const swapResponse = await sdk.functional.v0.pool.swap.swap(
  {
    runePubkey: "your-public-key",
    paymentPubkey: "your-payment-key",
    poolId: "pool-id",
    amountIn: "100000000",
    amountOut: "95000000",
    feeRate: 10,
    zeroToOne: true,
    exactIn: true,
    signedPsbt,
    splitRunePsbt: signedSplitRunePsbt,
    shardPubkeys: psbtResponse.data.shardPubkeys,
    signature,
  },
  connection
);

// Handle errors as shown previously

console.log("Swap executed with transaction ID:", swapResponse.data);
```

### Get Swap Details

```typescript
const swapDetailsResponse = await sdk.functional.v0.pool.swap.details.getSwapDetails(
  {
    token0: "token0-id",
    token1: "token1-id",
    amount: "100000000", // Amount in smallest units
    feeRate: 10,
    zeroToOne: true,
    exactIn: true,
  },
  connection
);

if (!swapDetailsResponse.success) {
  // Handle error
  console.error("Error:", swapDetailsResponse.data);
  throw new Error("Failed to get swap details");
}

const swapDetails = swapDetailsResponse.data;
console.log("Swap Details:", swapDetails);
```

- **Response Includes:**
  - `amountIn`, `amountOut`
  - `price`
  - `priceImpact`
  - `fees` (network and liquidity provider fees)
  - `poolId`

### Important Note on Swaps

When swapping from Rune to BTC:

- The `amountOut` returned by `getSwapDetails` does **not** account for network fees.
- You need to subtract network fees from `amountOut` before submitting your swap request.

## Error Handling

The SDK methods return an object indicating whether the request was successful. You should handle these responses appropriately in your application.

```typescript
const response = await sdk.functional.v0.pool.someMethod(params, connection);

if (!response.success) {
  // Handle error based on response status code and data
  if (response.status === 400) {
    console.error("Bad Request:", response.data);
  } else if (response.status === 401) {
    console.error("Unauthorized:", response.data);
  } else {
    console.error("Error:", response.data);
  }
  // Throw or handle the error as needed
  throw new Error("API request failed");
}

// Access the successful response data
const data = response.data;
```

## Advanced Topics

### Applying Slippage

To apply slippage to your swaps:

- Decide on the slippage percentage (e.g., 3%).
- Adjust the `amountOut` by subtracting the slippage percentage.

```typescript
const slippagePercentage = 0.03; // 3%

const adjustedAmountOut = BigInt(swapDetails.amountOut) * BigInt(1 - slippagePercentage * 10000n) / 10000n;

const swapResponse = await sdk.functional.v0.pool.swap.swap(
  {
    // ... other parameters
    amountOut: adjustedAmountOut.toString(),
  },
  connection
);
```

### Handling PSBTs

- Use a Bitcoin library compatible with PSBTs to sign the PSBTs provided by the SDK.
- Ensure you are using BIP322 for message signing.
- Use Taproot addresses for signatures.

```typescript
function signPsbt(psbtBase64: string, utxosToSign: UtxoToSign[]): string {
  // Decode the PSBT from base64
  const psbt = decodePsbt(psbtBase64);

  // Sign the PSBT using your wallet's functionality
  utxosToSign.forEach((utxo) => {
    // Use the signing indexes and sigHash from utxo to sign the inputs
    psbt.signInput(utxo.signingIndexes[0], yourKeyPair);
  });

  // Finalize the PSBT
  psbt.finalizeAllInputs();

  // Encode the signed PSBT back to base64
  return psbt.toBase64();
}

function signMessage(messageHash: string): string {
  // Sign the message hash using BIP322 with your Taproot address
  return wallet.signMessage(messageHash);
}
```

## Conclusion

The Pools SDK simplifies the process of interacting with the Pools API by handling request construction, serialization, and authentication. By following the steps outlined in this documentation, you can efficiently manage liquidity pools, perform token swaps, and integrate these functionalities into your applications.

If you encounter any issues or need further assistance, please refer to the official API documentation or contact support.