Wrapping BTC into wBTC

On the OP_NET metaprotocol, wrapping BTC allows you to convert native Bitcoin (BTC) into wrapped BTC (wBTC), which can then be used in smart contracts or traded within the OP_NET ecosystem. In this guide, you’ll learn how to wrap BTC using OP_NET’s transaction handling functionality.


Step 1: Install Required Packages

Ensure you have the following dependencies installed in your project:

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

Step 2: Setup the Provider and Wallet

Before wrapping BTC, set up the necessary provider and wallet configurations:

import { Wallet } from "@btc-vision/transaction";
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;

// Example of importing a wallet from its private key in WIF format
const walletWif = "your-wallet-private-key-in-wif-format";
const walletGet = Wallet.fromWif(walletWif, network);

console.log("Wallet Taproot Address:", walletGet.p2tr);


Step 4: Fetch Wrap Parameters

Now, fetch the wrapping parameters needed for the transaction:

import { OPNetLimitedProvider } from "@btc-vision/transaction";

/**
 * Fetches the wrapping parameters from the OP_NET RPC.
 *
 * @param wrapAmount The amount of BTC you want to wrap
 * @param rpcUrl The OP_NET RPC URL (e.g., regtest, mainnet)
 * @returns The wrap parameters required for the wrapping transaction
 */
async function fetchWrapParameters(wrapAmount: bigint, rpcUrl: string) {
  const provider = new OPNetLimitedProvider(rpcUrl);
  return await provider.fetchWrapParameters(wrapAmount);
}

Step 5: Wrap BTC

With the UTXOs and wrap parameters, you're ready to wrap BTC into wBTC:

import {
  currentConsensusConfig,
  IWrapParameters,
  TransactionFactory,
} from "@btc-vision/transaction";

/**
 * Wraps BTC into wBTC on the OP_NET metaprotocol.
 *
 * @param walletGet The wallet object (with private keys and addresses)
 * @param wrapAmount The amount of BTC to wrap in satoshis
 * @param network The Bitcoin network (e.g., regtest, mainnet)
 * @param provider The JSON RPC provider to communicate with OP_NET
 * @returns Transaction status
 */
async function wrapBTC(
  walletGet: Wallet,
  wrapAmount: bigint,
  network: bitcoinjs.Network,
  provider: JSONRpcProvider
) {
  try {
    const amountRequired =
      wrapAmount +
      currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES +
      50_000n;

    const utxos = await provider.utxoManager.getUTXOsForAmount({
      address: walletGet.p2tr,
      amount: wrapAmount,
    });

    if (!utxos || utxos.length === 0) {
      throw new Error("No UTXOs found for the wallet.");
    }

    // Fetch the wrapping parameters required for the transaction
    const generationParameters = await fetchWrapParameters(wrapAmount, rpcUrl);

    if (!generationParameters) {
      throw new Error("Failed to fetch wrap parameters.");
    }

    // Create wrap parameters for the transaction
    const wrapParameters: IWrapParameters = {
      from: walletGet.p2tr, // Sender's Taproot address
      utxos, // UTXOs for funding the transaction
      signer: walletGet.keypair, // Signer keypair for the transaction
      network, // The BitcoinJS network
      feeRate: 100, // Fee rate (satoshis per byte)
      priorityFee: 330n, // Priority fee for faster transaction
      amount: wrapAmount, // The amount of BTC to wrap
      generationParameters, // Wrapping-specific parameters
    };

    // Create and sign the wrap transaction
    const transactionFactory = new TransactionFactory();
    const finalTx = await transactionFactory.wrap(wrapParameters);

    // Broadcast the transaction
    const limitedProvider = new OPNetLimitedProvider(
      "https://regtest.opnet.org"
    ); // Initialize the OP_NET limited provider
    const firstTxBroadcast = await limitedProvider.broadcastTransaction(
      finalTx.transaction[0],
      false
    );
    if (!firstTxBroadcast || !firstTxBroadcast.success) {
      throw new Error("First transaction broadcast failed.");
    }

    const secondTxBroadcast = await limitedProvider.broadcastTransaction(
      finalTx.transaction[1],
      false
    );
    if (!secondTxBroadcast || !secondTxBroadcast.success) {
      throw new Error("Second transaction broadcast failed.");
    }

    return { success: true, txId: secondTxBroadcast.result };
  } catch (error) {
    console.error("Wrapping BTC failed:", error);
    return { success: false, message: error };
  }
}

// Example usage:
async function main() {
  const wrapAmount = 100_000n; // Amount in satoshis to wrap
  const result = await wrapBTC(walletGet, wrapAmount, network, provider);
  if (result.success) {
    console.log("Successfully wrapped BTC. Transaction ID:", result.txId);
  } else {
    console.error("Wrapping failed:", result.message);
  }
}

main();

Last updated