Wrapped BTC Contract

This page explains how to create a simple wrapped Bitcoin implementation using the Glittr SDK. The example demonstrates creating a contract that allows users to mint wrapped BTC tokens at a 1:1 ratio.

1. Setup and Configuration

import {
  Account,
  addFeeToTx,
  BitcoinUTXO,
  BlockTxTuple,
  electrumFetchNonGlittrUtxos,
  GlittrSDK,
  OpReturnMessage,
  Output,
  txBuilder
} from "@glittr-sdk/sdk";

const NETWORK = "regtest";
const client = new GlittrSDK({
  network: NETWORK,
  apiKey: <your api key here>,
  glittrApi: "https://devnet-core-api.glittr.fi",
  electrumApi: "https://devnet-electrum.glittr.fi"
});

First, we import the necessary components from the Glittr SDK and initialize the client with the appropriate network settings. In this case, we're using the regtest network for development purposes.

2. Account Setup

// Create two accounts: creator and minter
const creatorAccount = new Account({
  wif: "...",
  network: NETWORK,
});
const minterAccount = new Account({
  wif: "...",
  network: NETWORK,
});

We create two accounts:

  • creatorAccount: The account that will create and manage the contract

  • minterAccount: The account that will mint wrapped BTC tokens

3. Creating the Contract

async function deployWbtcContract() {
  const tx: OpReturnMessage = {
    contract_creation: {
      contract_type: {
        moa: {
          divisibility: 8,
          live_time: 0,
          supply_cap: 21000000n.toString(),
          mint_mechanism: {
            purchase: {
              input_asset: 'raw_btc',
              ratio: { fixed: { ratio: [1, 1] } } // 1:1 ratio
            }
          }
        }
      }
    }
  }

  const address = creatorAccount.p2pkh().address
  const utxos = await electrumFetchNonGlittrUtxos(client.electrumApi, client.apiKey, address)
  const nonFeeInputs: BitcoinUTXO[] = []
  const nonFeeOutputs: Output[] = [
    { script: txBuilder.compile(tx), value: 0 }, // Output #0 should always be OP_RETURN
    { address: address, value: 546 }
  ]

  const { inputs, outputs } = await addFeeToTx(NETWORK, address, utxos, nonFeeInputs, nonFeeOutputs)

  const txid = await client.createAndBroadcastRawTx({
    account: creatorAccount.p2pkh(),
    inputs,
    outputs
  })

  console.log(`TX: https://explorer.glittr.fi/tx/${txid}`);
}

The contract creation function sets up:

  • A maximum supply of 21 million (matching Bitcoin's cap)

  • 8 decimal places of divisibility (matching Bitcoin)

  • A 1:1 exchange ratio between BTC and the wrapped token

  • No time restrictions on minting/burning

4. Minting Wrapped BTC

async function mint() {
  // Change this to your deployWbtcContract() result
  const contract: BlockTxTuple = [101869, 1]; // https://explorer.glittr.fi/tx/688cbe5f4c147e46ef3ed2bbf448291c2041a7ab14ee9032ce1153b1ce89ed6e

  const tx: OpReturnMessage = {
    contract_call: {
      contract,
      call_type: {
        mint: {
          pointer: 1 // Points to the mint receiver's index in Output array 
        }
      }
    }
  }

  const address = minterAccount.p2pkh().address
  const utxos = await electrumFetchNonGlittrUtxos(client.electrumApi, client.apiKey, address)
  const nonFeeInputs: BitcoinUTXO[] = []
  const nonFeeOutputs: Output[] = [
    { script: txBuilder.compile(tx), value: 0 }, // Output #0 should always be OP_RETURN
    { address: address, value: 546 },
    { address: creatorAccount.p2pkh().address, value: 1000 } // Mint 1000 sats
  ]

  const { inputs, outputs } = await addFeeToTx(NETWORK, address, utxos, nonFeeInputs, nonFeeOutputs)

  const txid = await client.createAndBroadcastRawTx({
    account: minterAccount.p2pkh(),
    inputs,
    outputs
  })

  console.log(`TX: https://explorer.glittr.fi/tx/${txid}`);
}

The minting function:

  • References the existing contract

  • Creates a mint transaction

  • Sends Bitcoin to the creator's address

  • Receives wrapped BTC tokens in return

5. Checking Asset Details

async function _checkingAsset() {
  const mintTxid =
    "a320545261eb503ba305ebaf3e7bcaa7534c905b91b03a51759cf8e8128808de";
  const mintVout = 0;
  const result = await fetch(
    `https://devnet-core-api.glittr.fi/assets/${mintTxid}/${mintVout}`
  );

  console.log(await result.text());
}

This utility function allows you to check the details of an asset by its transaction ID and output index.

Key Concepts

  1. 1:1 Backing: Each wrapped BTC token is backed by an equivalent amount of Bitcoin.

  2. Supply Cap: The contract matches Bitcoin's supply cap of 21 million.

  3. Divisibility: The wrapped token maintains the same 8 decimal places as Bitcoin.

  4. Purchase/Burn: Users can mint tokens by sending BTC and burn tokens to receive BTC back.

This implementation provides a simple way to create wrapped Bitcoin tokens that maintain a 1:1 peg with actual Bitcoin, allowing for easier integration with other Glittr contracts and applications.

Usage Flow

  1. Initialize the SDK and accounts

    const client = new GlittrSDK({...});
    const creatorAccount = new Account({...});
    const minterAccount = new Account({...});
  2. Create the wrapped BTC contract

    await deployWbtcContract();
  3. Mint wrapped BTC tokens

    await mint();
  4. Check asset details

    await _checkingAsset();

Full Code Example

import {
  Account,
  addFeeToTx,
  BitcoinUTXO,
  BlockTxTuple,
  electrumFetchNonGlittrUtxos,
  GlittrSDK,
  OpReturnMessage,
  Output,
  txBuilder
} from "@glittr-sdk/sdk";

const NETWORK = "regtest";
const client = new GlittrSDK({
  network: NETWORK,
  apiKey: <your api key here>,
  glittrApi: "https://devnet-core-api.glittr.fi", // devnet
  electrumApi: "https://devnet-electrum.glittr.fi" // devnet
});

const creatorAccount = new Account({
  wif: "...",
  network: NETWORK,
});

const minterAccount = new Account({
  wif: "...",
  network: NETWORK,
});

console.log(`Creator account ${creatorAccount.p2pkh().address}`);
console.log(`Minter account ${minterAccount.p2pkh().address}`);


async function deployWbtcContract() {
  const tx: OpReturnMessage = {
    contract_creation: {
      contract_type: {
        moa: {
          divisibility: 8,
          live_time: 0,
          supply_cap: 21000000n.toString(),
          mint_mechanism: {
            purchase: {
              input_asset: 'raw_btc',
              ratio: { fixed: { ratio: [1, 1] } } // 1:1 ratio
            }
          }
        }
      }
    }
  }

  const address = creatorAccount.p2pkh().address
  const utxos = await electrumFetchNonGlittrUtxos(client.electrumApi, client.apiKey, address)
  const nonFeeInputs: BitcoinUTXO[] = []
  const nonFeeOutputs: Output[] = [
    { script: txBuilder.compile(tx), value: 0 }, // Output #0 should always be OP_RETURN
    { address: address, value: 546 }
  ]

  const { inputs, outputs } = await addFeeToTx(NETWORK, address, utxos, nonFeeInputs, nonFeeOutputs)

  const txid = await client.createAndBroadcastRawTx({
    account: creatorAccount.p2pkh(),
    inputs,
    outputs
  })

  console.log(`TXID : ${txid}`)
}

async function mint() {
  // Change this to your deployWbtcContract() result
  const contract: BlockTxTuple = [101869, 1]; // https://explorer.glittr.fi/tx/688cbe5f4c147e46ef3ed2bbf448291c2041a7ab14ee9032ce1153b1ce89ed6e

  const tx: OpReturnMessage = {
    contract_call: {
      contract,
      call_type: {
        mint: {
          pointer: 1 // Points to the mint receiver's index in Output array 
        }
      }
    }
  }

  const address = minterAccount.p2pkh().address
  const utxos = await electrumFetchNonGlittrUtxos(client.electrumApi, client.apiKey, address)
  const nonFeeInputs: BitcoinUTXO[] = []
  const nonFeeOutputs: Output[] = [
    { script: txBuilder.compile(tx), value: 0 }, // Output #0 should always be OP_RETURN
    { address: address, value: 546 },
    { address: creatorAccount.p2pkh().address, value: 1000 } // Mint 1000 sats
  ]

  const { inputs, outputs } = await addFeeToTx(NETWORK, address, utxos, nonFeeInputs, nonFeeOutputs)

  const txid = await client.createAndBroadcastRawTx({
    account: minterAccount.p2pkh(),
    inputs,
    outputs
  })

  console.log(`TXID : ${txid}`)
}

/**
 * Call deployWbtcContract() function first
 * wait for confirmation and indexing
 * then call the mint() function
 */
deployWbtcContract()



/**
 * Function to check asset after mint
 * change the mintTxId and mintVout
 * with your vestedMint() and freeMint() result
 */
async function _checkingAsset() {
  const mintTxid =
    "a320545261eb503ba305ebaf3e7bcaa7534c905b91b03a51759cf8e8128808de";
  const mintVout = 0;
  const result = await fetch(
    `https://devnet-core-api.glittr.fi/assets/${mintTxid}/${mintVout}`
  );

  console.log(await result.text());
}

Last updated