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
Copy 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
Copy // 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
Copy async function deployWbtcContract () {
const tx : OpReturnMessage = {
contract_creation : {
contract_type : {
moa : {
divisibility : 8 ,
live_time : 0 ,
supply_cap : 21000000 n .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
Copy 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
Copy 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 Backing : Each wrapped BTC token is backed by an equivalent amount of Bitcoin.
Supply Cap : The contract matches Bitcoin's supply cap of 21 million.
Divisibility : The wrapped token maintains the same 8 decimal places as Bitcoin.
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
Initialize the SDK and accounts
Copy const client = new GlittrSDK ({ ... });
const creatorAccount = new Account ({ ... });
const minterAccount = new Account ({ ... });
Create the wrapped BTC contract
Copy await deployWbtcContract ();
Check asset details
Copy await _checkingAsset ();
Full Code Example
Copy 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 : 21000000 n .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 ());
}