Skip to content

octopus-network/ree-client-ts-sdk

Repository files navigation

Ree Client TypeScript SDK

A TypeScript SDK for interacting with the Ree protocol on Bitcoin, providing seamless integration with Bitcoin wallets and Rune tokens.

Features

  • 🔗 Bitcoin Integration: Connect with Bitcoin wallets and manage UTXOs
  • 🪙 Rune Support: Full support for Bitcoin Runes protocol
  • ⚛️ React Integration: Built-in React hooks and providers
  • 🔧 TypeScript: Full type safety and IntelliSense support
  • 🧪 Well Tested: Comprehensive test coverage
  • 📦 Lightweight: Minimal dependencies and optimized bundle size
  • 🔄 Multi-Intention Transactions: Support for complex transactions with multiple operations

Installation

npm install @omnity/ree-client-ts-sdk

Quick Start

Initializing the Client

import { ReeClient, Network, type Config } from "@omnity/ree-client-ts-sdk";

const config: Config = {
  network: Network.Testnet, // or Network.Mainnet
  xverseApiKey: "your-xverse-api-key",
  exchangeIdlFactory: yourExchangeIdlFactory,
  exchangeCanisterId: "your-exchange-canister-id",
  exchangeId: "your-exchange-id",
};

const client = new ReeClient(config);

Basic Usage

// Get Bitcoin UTXOs
const utxos = await client.getBtcUtxos("bc1q..."); // payment address

// Get Rune UTXOs
const runeUtxos = await client.getRuneUtxos("bc1q...", "RUNE_ID"); // address, runeId

// Get Rune Info
const runeInfo = await client.getRuneInfo("RUNE_ID");

// Get Bitcoin Balance
const btcBalance = await client.getBtcBalance("bc1q..."); // payment address

const runeBalance = await client.getRuneBalance("bc1q...", "RUNE_ID"); // address, runeId

// Get pool information
const pools = await client.getPoolList();
const poolInfo = await client.getPoolInfo("pool-address");

Creating and Executing Transactions

Simple Single-Pool Transaction

// Create a transaction for a single pool operation
const transaction = await client.createTransaction({
  address: "bc1p...", // Bitcoin address for runes
  paymentAddress: "bc1q...", // Payment address for BTC
  // feeRate: 25, // Optional manual fee rate in sat/vbyte
  // mergeSelfRuneBtcOutputs: true, // Optional: reuse BTC output when your rune address also receives sats
});

// By default, runes delivered back to your own rune address are kept on a separate
// dust output to avoid mixing with BTC change. Set `mergeSelfRuneBtcOutputs` to true
// if you prefer to consolidate the runes and sats into the same output.

// Add a single intention (e.g., swap BTC for runes)
transaction.addIntention({
  poolAddress: "bc1p...",
  poolUtxos: [],
  inputCoins: [
    {
      coin: {
        id: "0:0",
        value: BigInt(100000),
      }, // Send 0.001 BTC,
      from: paymentAddress,
    },
  ],
  outputCoins: [
    {
      coin: {
        id: "840000:3",
        value: BigInt(1000),
      }, // Receive 1000 runes,
      to: address,
    },
  ],
  action: "swap",
  nonce: BigInt(1234),
});

// Build and execute
const { psbt } = await transaction.build();
const signedPsbt = await wallet.signPsbt(psbt);
const result = await transaction.send(signedPsbt.toHex());

Advanced Multi-Intention Transaction

// Create a complex transaction with multiple operations
const transaction = await client.createTransaction({
  address: "bc1p...",
  paymentAddress: "bc1q...",
  // feeRate: 25,
});

// Add multiple intentions in a single transaction
// Intention 1: Deposit BTC to Pool 1
transaction.addIntention({
  poolAddress: "bc1p...pool1",
  poolUtxos: [],
  inputCoins: [
    {
      // Deposit 0.0005 BTC
      coin: {
        id: "0:0",
        value: BigInt(50000),
      },
      from: paymentAddress,
    },
  ],
  outputCoins: [],
  action: "deposit",
  nonce: BigInt(Date.now()),
});

// Intention 2: Swap runes between pools
transaction.addIntention({
  poolAddress: "bc1q...pool2",
  poolUtxos: [],
  inputCoins: [
    {
      // Send 500 of rune A,
      coin: {
        id: "840000:3",
        value: BigInt(500),
      },
      from: address,
    },
  ],
  outputCoins: [
    {
      // Receive 250 of rune B,
      coin: {
        id: "840000:5",
        value: BigInt(250),
      },
      to: address,
    },
  ],
  action: "swap",
  nonce: BigInt(Date.now() + 1),
});

// Build and execute the multi-intention transaction
const { psbt } = await transaction.build();
const signedPsbt = await wallet.signPsbt(psbt);
const result = await transaction.send(signedPsbt.toHex());

React Integration

import { ReeProvider, useRee, useBtcBalance } from "@omnity/ree-client-ts-sdk";

function App() {
  return (
    <ReeProvider config={config}>
      <WalletComponent />
    </ReeProvider>
  );
}

function WalletComponent() {
  const { client, address, paymentAddress, updateWallet, createTransaction } = useRee();
  const { balance: btcBalance } = useBtcBalance();

  const connectWallet = () => {
    updateWallet({
      address: "bc1q...",
      paymentAddress: "bc1q...",
    });
  };

  const executeComplexTransaction = async () => {
    // Create transaction with multiple pools
    const tx = await createTransaction({
      feeRate: 25, // Optional manual fee rate override
    });

    // Add multiple intentions
    tx.addIntention({
      poolAddress: "pool1",
      poolUtxos: [],
      inputCoins: [{
        coin: {
          id: "0:0",
          value: BigInt(100000)
        },
        from: paymentAddress
      }],
      outputCoins: [
        coint: {
          id: "840000:3",
          value: BigInt(1000)
        },
        to: address
      ],
      action: "swap",
      nonce: BigInt(Date.now()),
    });

    tx.addIntention({
      poolAddress: "pool2",
      poolUtxos: [],
      inputCoins: [{
        coin: {
          id: "840000:3",
          value: BigInt(500)
        },
        from: address
      }],
      outputCoins: [{
        coin: {
          id: "0:0",
          value: BigInt(50000)
        },
        to: paymentAddress
      }],
      action: "swap",
      nonce: BigInt(Date.now() + 1),
    });

    const { psbt } = await tx.build();
    // Sign and send...
  };

  return (
    <div>
      <div>Connected: {address}</div>
      <div>Balance: {btcBalance} BTC</div>
      <button onClick={connectWallet}>Connect wallet</button>
      <button onClick={executeComplexTransaction}>
        Execute Complex Transaction
      </button>
    </div>
  );
}

Integration with LaserEyes

import { LaserEyesProvider, useLaserEyes } from "@omnisat/lasereyes-core";
import { ReeProvider, useRee } from "@ree-network/ts-sdk";

function App() {
  return (
    <LaserEyesProvider config={laserEyesConfig}>
      <ReeProvider config={reeConfig}>
        <ConnectWalletModal />
        <MyComponent />
      </ReeProvider>
    </LaserEyesProvider>
  );
}

function MyComponent({ children }) {
  const { signPsbt } = useLaserEyes();
  const { updateWallet, createTransaction, exchange } = useRee();

  const sendTransaction = async () => {
    const depositBtcAmount = BigInt(100000);
    const depositOffer = await exchange.pre_deposit("bc1q...", {
      id: "0:0",
      value: depositBtcAmount,
    });

    const tx = await createTransaction({
      // feeRate: BigInt(25),
    });

    tx.addIntention({
      poolAddress: "bc1q...",
      poolUtxos: [],
      inputCoins: [
        {
          coin: {
            id: "0:0",
            value: depositBtcAmount,
          },
          from: paymentAddress,
        },
      ],
      outputCoins: [],
      action: "deposit",
      nonce: depositOffer.nonce,
    });

    const { psbt } = await tx.build();

    const signedPsbt = await signPsbt(psbt);
    const txid = await tx.send(signedPsbt.toHex());

    console.log("Transaction sent:", txid);
  };

  return (
    <div>
      <button onClick={sendTransaction}>Send Transaction</button>
    </div>
  );
}

function ConnectWalletModal() {
  const { address, paymentAddress, connect, publicKey } = useLaserEyes();
  const { updateWallet } = useRee();

  useEffect(() => {
    updateWallet({
      address,
      paymentAddress,
      publicKey,
    });
  }, [address, paymentAddress, updateWallet]);

  return (
    <div>
      <button onClick={connect}>Connect Wallet</button>
    </div>
  );
}

API Reference

ReeClient

The main client class for interacting with the Ree protocol.

Constructor

new ReeClient(config: Config)

Methods

Balance & UTXO Methods
  • getBtcBalance(paymentAddress: string): Promise<number> - Get Bitcoin balance in BTC
  • getBtcUtxos(paymentAddress: string): Promise<Utxo[]> - Get Bitcoin UTXOs for the payment address
  • getRuneBalance(address: string, runeId: string): Promise<number | undefined> - Get balance for a specific rune
  • getRuneUtxos(address: string, runeId: string): Promise<Utxo[]> - Get UTXOs containing a specific rune
Pool Methods
  • getPoolList(): Promise<Pool[]> - Get list of all available liquidity pools
  • getPoolInfo(poolAddress: string): Promise<PoolInfo> - Get information about a specific liquidity pool
Transaction Methods
  • createTransaction(params?: { feeRate?: number }): Promise<Transaction> - Create a transaction with optional manual fee rate

Transaction

Transaction builder for Bitcoin and Rune transactions with multi-intention support.

Methods

  • addIntention(intention: Intention): void - Add an intention to the transaction
  • build(): Promise<{ psbt: bitcoin.Psbt, txid: string }> - Build the PSBT with all intentions
  • send(signedPsbtHex: string): Promise<any> - Submit the signed transaction

Intention Structure

interface Intention {
  // Target pool address
  poolAddress: string;
  // the UTXOs obtained through the pool’s pre methods
  poolUtxos?: [];
  // Coins being sent to the pool
  inputCoins: InputCoin[];
  // Coins expected from the pool
  outputCoins: InputCoin[];
  // Action type (swap, deposit, withdraw, etc.)
  action: string;
  // Optional action parameters
  actionParams?: string;
  // Unique nonce for the intention
  nonce: bigint;
}

type CoinBalance = {
  id: string; // Coin ID ("0:0" for BTC, "840000:3" for runes)
  value: bigint; // Amount in smallest unit
};

type InputCoin = {
  coin: CoinBalance;
  from: string;
};

type OutputCoin = {
  coin: CoinBalance;
  to: string;
};

Multi-Intention Transaction Benefits

  1. Atomic Operations: All intentions succeed or fail together
  2. Gas Efficiency: Single transaction fee for multiple operations
  3. Complex Strategies: Enable sophisticated trading strategies
  4. Cross-Pool Operations: Interact with multiple pools in one transaction

Transaction Flow

  1. Create Transaction: Specify involved pools and runes
  2. Add Intentions: Define each operation you want to perform
  3. Build PSBT: SDK calculates all inputs/outputs and fees
  4. Sign: Use your wallet to sign the transaction
  5. Submit: Send to the network for execution

All intentions in a transaction are processed atomically - if any intention fails, the entire transaction is reverted.

React Hooks

The SDK provides specialized hooks for all operations:

import {
  useBtcBalance,
  useRuneBalance,
  useBtcUtxos,
  useRuneUtxos,
  useRuneInfo,
  usePoolList,
  usePoolInfo,
  useRecommendedFeeRate,
} from "@ree-network/ts-sdk";

function TradingDashboard() {
  // Balance & UTXO hooks
  const { balance: btcBalance, loading: btcLoading } = useBtcBalance();
  const { balance: runeBalance } = useRuneBalance("840000:3");
  const { utxos: btcUtxos } = useBtcUtxos();
  const { utxos: runeUtxos } = useRuneUtxos("840000:3");
  const {
    feeRate,
    loading: feeRateLoading,
    refetch: refreshFeeRate,
  } = useRecommendedFeeRate();

  const [runes, setRunes] = useState([]);

  // Pool hooks
  const { pools } = usePoolList();
  const { poolInfo } = usePoolInfo("bc1q...");

  return (
    <div>
      <h2>Balances</h2>
      <div>BTC: {btcBalance} BTC</div>
      <div>Rune: {runeBalance}</div>
      <div>
        Recommended Fee Rate:{" "}
        {feeRateLoading
          ? "Loading..."
          : `${feeRate?.min?.toString()} - ${feeRate?.max?.toString()} sat/vB`}
      </div>

      <h2>Pools</h2>
      <div>{pools.length} pools available</div>
    </div>
  );
}

Available Hooks

Balance & UTXO Hooks:

  • useBtcBalance(options?) - Bitcoin balance management
  • useRuneBalance(runeId, options?) - Rune balance for specific rune
  • useBtcUtxos(options?) - Bitcoin UTXOs management
  • useRuneUtxos(runeId, options?) - Rune UTXOs for specific rune

Pool Hooks:

  • usePoolList(options?) - Get all available pools
  • usePoolInfo(poolAddress?, options?) - Get specific pool information

Fee Rate Hooks:

  • useRecommendedFeeRate(options?) - Retrieve recommended fee rate range

Hook Usage Examples

// Get rune info with polling
const { runeInfo } = useRuneInfo("840000:3", { refreshInterval: 30000 });

// Get pools with manual refresh only
const { pools, refetch } = usePoolList({ autoRefresh: false });

Core useRee Hook

The useRee() hook now focuses on core functionality:

const {
  client, // Direct access to ReeClient
  address, // Current Bitcoin address
  paymentAddress, // Current payment address
  updateWallet, // Update wallet addresses
  exchange, // Exchange canister actor
  createTransaction, // Create transactions
} = useRee();

Types

The SDK exports all necessary TypeScript types:

import type {
  Config,
  Utxo,
  RuneInfo,
  Pool,
  PoolInfo,
  IntentionSet,
  Intention,
  TransactionConfig,
  AddressType,
} from "@ree-network/ts-sdk";

Configuration

interface Config {
  network: Network; // Network.Mainnet or Network.Testnet
  xverseApiKey: string; // Your Xverse API key
  exchangeIdlFactory: IDL.InterfaceFactory; // Exchange canister IDL
  exchangeCanisterId: string; // Exchange canister ID
  exchangeId: string; // Exchange ID for transactions
}

Networks

enum Network {
  Mainnet = "mainnet",
  Testnet = "testnet",
}

About

A TypeScript SDK for interacting with the Ree protocol on Bitcoin, providing seamless integration with Bitcoin wallets and Rune tokens.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors