Swapping Tokens on OP_NET

Here’s how to set up a token swap using OP_NET. This involves approving the tokens and executing a swap between two tokens (e.g., wBTC and another OP_20 token) on the OP_NET metaprotocol using a decentralized exchange like Motoswap.


Step 1: Install Required Packages

Ensure you have the following dependencies installed:

npm install opnet bitcoinjs-lib
# or
yarn add opnet bitcoinjs-lib

Step 2: Set Up the Provider

Initialize the provider to communicate with the OP_NET metaprotocol:

import * as bitcoinjs from "bitcoinjs-lib";
import { JSONRpcProvider } from "opnet";

// Define the OP_NET RPC provider
const rpcUrl = "https://regtest.opnet.org";
const provider = new JSONRpcProvider(rpcUrl);

// Define the Bitcoin network (e.g., regtest, mainnet)
const network = bitcoinjs.networks.regtest;



Step 5: Perform the Swap

import {
  IInteractionParameters,
  TransactionFactory,
  Wallet,
} from "@btc-vision/transaction";
import {
  getContract,
  IMotoswapRouterContract,
  MOTOSWAP_ROUTER_ABI,
} from "opnet";

/**
 * Swaps tokens using a decentralized exchange on the OP_NET metaprotocol.
 *
 * @param {JSONRpcProvider} provider - The JSON RPC provider for interacting with OP_NET.
 * @param {Wallet} walletGet - The wallet object with keypairs and addresses.
 * @param {bigint} amountIn - The amount of tokens to swap in (in satoshis).
 * @param {string} inputAddress - The address of the token to swap from (input token).
 * @param {string} outputAddress - The address of the token to swap to (output token).
 * @param {bitcoinjs.Network} network - The BitcoinJS network (e.g., regtest, mainnet).
 * @param {number} slippagePercentage - The acceptable slippage percentage for the swap.
 * @returns The transaction result.
 */
async function swapTokens(
  provider: JSONRpcProvider,
  walletGet: Wallet,
  amountIn: bigint,
  inputAddress: string,
  outputAddress: string,
  network: bitcoinjs.Network,
  slippagePercentage: number
) {
  try {
    // Actual router address on regtest
    const routerAddress = "bcrt1qplnz54sca73t8a03nh494jatr9ffjg6ecarrj8";

    // Get the swap contract from Motoswap using the router address
    const getSwap = getContract<IMotoswapRouterContract>(
      routerAddress,
      MOTOSWAP_ROUTER_ABI,
      provider,
      walletGet.p2tr
    );

    const maxUint256 = BigInt(
      "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
    );

    // Fetch UTXOs for the wallet's addresses
    const utxos = await getUTXOs(walletGet.addresses, rpcUrl, maxUint256);

    // Approve the input token (wBTC) for the transaction
    const approvedData = await approveToken(
      walletGet,
      inputAddress,
      amountIn,
      utxos
    );

    if (!approvedData) {
      return {
        success: false,
        message: "An error occurred while approving the token.",
      };
    }

    // Approve the output token for the swap
    const nextUtxos = await approveToken(
      walletGet,
      outputAddress,
      amountIn,
      approvedData
    );

    if (!nextUtxos) {
      return {
        success: false,
        message: "An error occurred while approving the token.",
      };
    }

    // Get the estimated amount out based on the input token amount
    const amountOut = await getSwap.getAmountsOut(amountIn, [
      inputAddress,
      outputAddress,
    ]);

    if ("error" in amountOut) {
      return { success: false, message: amountOut.error };
    }

    // Calculate minimum amount out with slippage tolerance
    const amountOutMin = amountOut.decoded[0].toString().split(",")[1];
    const amountOutBigInt = BigInt(amountOutMin);
    const finalAmountOutWithSlippage =
      amountOutBigInt - (amountOutBigInt * BigInt(slippagePercentage)) / 100n;

    // Set the transaction deadline (current time + 2 minutes)
    const deadline = Math.floor(Date.now() / 1000) + 60 * 2;

    // Prepare the calldata for the swap transaction
    const contractResult = getSwap.encodeCalldata(
      "swapExactTokensForTokensSupportingFeeOnTransferTokens",
      [
        amountIn,
        finalAmountOutWithSlippage,
        [inputAddress, outputAddress],
        walletGet.p2tr,
        BigInt(deadline),
      ]
    );

    // Set up transaction parameters for the interaction
    const interactionParameters: IInteractionParameters = {
      from: walletGet.p2tr,
      to: routerAddress,
      utxos: nextUtxos,
      signer: walletGet.keypair,
      network,
      feeRate: 100,
      priorityFee: 9500n,
      calldata: contractResult as Buffer,
    };

    // Sign and broadcast the transaction
    const transactionFactory = new TransactionFactory();
    const sendTransact = await transactionFactory.signInteraction(
      interactionParameters
    );

    const firstTransaction = await provider.sendRawTransaction(
      sendTransact[0],
      false
    );
    if (!firstTransaction.success) {
      return {
        success: false,
        message: "Could not broadcast first transaction",
      };
    }

    const secondTransaction = await provider.sendRawTransaction(
      sendTransact[1],
      false
    );
    if (!secondTransaction.success) {
      return {
        success: false,
        message: "Could not broadcast second transaction",
      };
    }

    return { success: true, txId: secondTransaction.result, amountOutBigInt };
  } catch (error) {
    console.error("Token swap failed:", error);
    return {
      success: false,
      message: "An error occurred. Please try again.",
    };
  }
}

// Example usage:
async function main() {
  const walletWif = "your-wallet-private-key-in-wif-format"; // Replace with your WIF private key
  const walletGet = Wallet.fromWif(walletWif, network); // Import wallet using WIF format
  const inputAddress = "bcrt1qdr7sjgtnudda8zrfklw8l5cnrxum5hns7e46hf"; // Actual wBTC address on regtest
  const outputAddress = "your-token-address"; // Replace with your token's address
  const amountIn = 100_000n; // Amount of tokens to swap in satoshis
  const slippagePercentage = 2; // Slippage tolerance percentage

  const result = await swapTokens(
    provider,
    walletGet,
    amountIn,
    inputAddress,
    outputAddress,
    network,
    slippagePercentage
  );
  if (result.success) {
    console.log("Token successfully swapped. Transaction ID:", result.txId);
  } else {
    console.error("Swap failed:", result.message);
  }
}

main();

Last updated