USD Contract

This page explains how to create a USD-pegged token using the Glittr SDK with oracle-based price feeds. The token's mint price will be determined by the current BTC/USD exchange rate.

1. Setup and Configuration

import {
  Account,
  GlittrSDK,
  OpReturnMessage,
  electrumFetchNonGlittrUtxos,
  BitcoinUTXO,
  Output,
  addFeeToTx,
  txBuilder,
  BlockTxTuple,
} from "@glittr-sdk/sdk";
import {
  OracleMessage,
  OracleMessageSigned,
} from "@glittr-sdk/sdk/dist/transaction/calltype/types";
import { schnorr, getPublicKey } from "@noble/secp256k1";
import { sha256 } from "bitcoinjs-lib/src/crypto";

const NETWORK = "regtest";
const client = new GlittrSDK({
  network: NETWORK,
  apiKey: '1c4938fb-1a10-48c2-82eb-bd34eeb05b20',
  glittrApi: "https://devnet-core-api.glittr.fi", // devnet
  electrumApi: "https://devnet-electrum.glittr.fi" // devnet
});

We start by setting up the SDK client for the regtest network (development environment) and import necessary functions.

2. Account and Oracle Setup

// Create accounts for different roles
const creatorAccount = new Account({
  wif: "...",
  network: NETWORK,
});

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

// Setup oracle for price feeds
const oraclePrivateKey = new Uint8Array([...]);
const oraclePubkey = Array.from(getPublicKey(oraclePrivateKey, true)).slice(1);

We set up:

  • A creator account that will deploy the contract

  • A minter account that will mint tokens

  • An oracle account that will provide BTC/USD price feeds

3. Creating the USD-Pegged Contract

async function deployUsdContract() {
  const tx: OpReturnMessage = {
    contract_creation: {
      contract_type: {
        moa: {
          divisibility: 1,
          live_time: 0,
          mint_mechanism: {
            purchase: {
              input_asset: 'raw_btc',
              ratio: {
                oracle: {
                  setting: {
                    pubkey: oraclePubkey,
                    asset_id: 'btc',
                    block_height_slippage: 5
                  }
                }
              }
            }
          }
        }
      }
    }
  }

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

  const { inputs, outputs } = await addFeeToTx(NETWORK, creatorAccount.p2pkh().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:

  • Sets up a token with 1 decimal place precision

  • Uses an oracle for price determination

  • Allows a 5-block window for price updates

  • Sends BTC to the creator's address during mints

4. Minting USD-Pegged Tokens

async function mint() {
  const contract: BlockTxTuple = [101999, 1]

  // Get block height
  const blockHeightFetch = await fetch(`${client.electrumApi}/blocks/tip/height`)
  const blockHeight = await blockHeightFetch.json()

  // 1 BTC = 70000 usd
  // 1 sat = 0.0007
  // with divisibilty 8 => 1 sat = 70000
  // times 10**-8 for display

  const oracleMessage: OracleMessage = {
    asset_id: "btc",
    ratio: [70000, 1], // 1 sats = 70000 asset
    block_height: blockHeight,
  };

  const signature = await schnorr.sign(
    sha256(Buffer.from(JSON.stringify(oracleMessage), "ascii")).toString("hex"),
    oraclePrivateKey
  );

  const oracleSignedMessage: OracleMessageSigned = {
    signature: Array.from(signature),
    message: oracleMessage,
  };

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

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

  const { inputs, outputs } = await addFeeToTx(NETWORK, minterAccount.p2pkh().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 process:

  1. Gets current block height

  2. Creates an oracle message with the BTC/USD price

  3. Signs the message with the oracle's private key

  4. Creates and broadcasts a mint transaction

5. Checking Asset Details

async function _checkingAsset() {
  const mintTxid = "cece25c03d7d2c1a297eab6eff89965989259953c0b55e08c3f1370a0ecdfdc8"
  const mintVout = 0
  const assetFetch = await fetch(`${client.glittrApi}/assets/${mintTxid}/${mintVout}`)
  const asset = await assetFetch.text()

  console.log(`Asset : ${asset}`)
}

This function allows you to check the details of minted assets.

Key Concepts

  1. Oracle-Based Pricing: The contract uses an oracle to determine the BTC/USD exchange rate

  2. Price Updates: Allows a 5-block window for price updates to account for network delays

  3. USD Pegging: Tokens are minted based on the current BTC/USD rate

  4. Example Rate: In this example, 1 satoshi = 0.0007 USD (BTC price of ~$70,000)

This implementation creates a USD-pegged token where users can mint tokens by sending BTC, with the amount of tokens received based on the current BTC/USD exchange rate provided by the oracle.

Usage Flow

  1. Setup

    // Initialize SDK and accounts
    const client = new GlittrSDK({...});
    const creatorAccount = new Account({...});
    const minterAccount = new Account({...});
  2. Create Contract

    await deployUsdContract();
  3. Mint Tokens

    await mint();
  4. Check Asset Details

    await _checkingAsset();

Full Code Example

import {
  Account,
  GlittrSDK,
  OpReturnMessage,
  electrumFetchNonGlittrUtxos,
  BitcoinUTXO,
  Output,
  addFeeToTx,
  txBuilder,
  BlockTxTuple,
} from "@glittr-sdk/sdk";
import {
  OracleMessage,
  OracleMessageSigned,
} from "@glittr-sdk/sdk/dist/transaction/calltype/types";
import { schnorr, getPublicKey } from "@noble/secp256k1";
import { sha256 } from "bitcoinjs-lib/src/crypto";

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,
});

// ORACLE
// Generate a random private key
const oraclePrivateKey = new Uint8Array([...]);

// Get the corresponding public key
const oraclePubkey = Array.from(getPublicKey(oraclePrivateKey, true)).slice(1);
console.log("Oracle public key:", Buffer.from(oraclePubkey).toString("hex"));

async function deployUsdContract() {
  const tx: OpReturnMessage = {
    contract_creation: {
      contract_type: {
        moa: {
          divisibility: 1,
          live_time: 0,
          mint_mechanism: {
            purchase: {
              input_asset: 'raw_btc',
              ratio: {
                oracle: {
                  setting: {
                    pubkey: oraclePubkey,
                    asset_id: 'btc',
                    block_height_slippage: 5
                  }
                }
              }
            }
          }
        }
      }
    }
  }

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

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

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

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

async function mint() {
  const contract: BlockTxTuple = [101999, 1]

  // Get block height
  const blockHeightFetch = await fetch(`${client.electrumApi}/blocks/tip/height`)
  const blockHeight = await blockHeightFetch.json()

  // 1 BTC = 70000 usd
  // 1 sat = 0.0007
  // with divisibilty 8 => 1 sat = 70000
  // times 10**-8 for display

  const oracleMessage: OracleMessage = {
    asset_id: "btc",
    ratio: [70000, 1], // 1 sats = 70000 asset
    block_height: blockHeight,
  };

  const signature = await schnorr.sign(
    sha256(Buffer.from(JSON.stringify(oracleMessage), "ascii")).toString("hex"),
    oraclePrivateKey
  );

  const oracleSignedMessage: OracleMessageSigned = {
    signature: Array.from(signature),
    message: oracleMessage,
  };

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

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

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

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

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

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


/**
 * Function to check asset after mint
 * change the mintTxId and mintVout
 * with your mint() fnction result
 */
async function _checkingAsset() {
  const mintTxid = "cece25c03d7d2c1a297eab6eff89965989259953c0b55e08c3f1370a0ecdfdc8"
  const mintVout = 0
  const assetFetch = await fetch(`${client.glittrApi}/assets/${mintTxid}/${mintVout}`)
  const asset = await assetFetch.text()

  console.log(`Asset : ${asset}`)
}

Last updated