Skip to main content

frBTC on Alkanes

frBTC is the native Bitcoin synthetic token on the Alkanes metaprotocol, pegged 1:1 to BTC. This guide covers wrapping, unwrapping, and interacting with the frBTC contract using Alkanes.

Contract Location

PropertyValue
Alkane ID{32, 0}
Block32
Transaction0

The frBTC contract is deployed at genesis and is immutable.

Contract Opcodes

The frBTC contract exposes these methods via opcodes:

Core Operations

OpcodeMethodParametersDescription
77exchange()NoneWrap BTC to frBTC
78unwrap()vout: u128, amount: u128Burn frBTC and queue BTC payment

Query Methods

OpcodeMethodReturnsDescription
99get_name()"Fractional BTC"Token name
100get_symbol()"frBTC"Token symbol
101get_pending_unwraps()Payment[]List pending payments
102get_decimals()8Decimal places
103get_signer()[u8; 32]Current signer pubkey
105get_total_supply()u128Total frBTC supply

How Wrapping Works

Wrapping converts native BTC to frBTC in a single atomic Bitcoin transaction:

┌─────────────────────────────────────────────────────────┐
│ WRAP TRANSACTION │
├─────────────────────────────────────────────────────────┤
│ Input: Your BTC UTXO │
├─────────────────────────────────────────────────────────┤
│ Output 0: BTC → SUBFROST signer address │
│ Output 1: frBTC → Your address (via protostone) │
│ Output 2+: Change → Your change address │
├─────────────────────────────────────────────────────────┤
│ OP_RETURN: Protostone calling exchange() opcode 77 │
└─────────────────────────────────────────────────────────┘

The frBTC contract at {32, 0} validates that BTC was sent to the correct signer address and mints an equivalent amount of frBTC to your address.

Wrap Using CLI

alkanes alkanes wrap-btc <AMOUNT> \
--to <RECIPIENT_ADDRESS> \
--from <YOUR_ADDRESS> \
--change <CHANGE_ADDRESS> \
--fee-rate <SAT_PER_VB> \
-y

Example: Wrap 0.01 BTC

alkanes alkanes wrap-btc 0.01 \
--to bc1p... \
--from bc1q... \
--change bc1q... \
--fee-rate 10 \
-y

Advanced: Execute with Protostone

For custom transactions:

alkanes alkanes execute \
--inputs "B:1000000" \
--to "<SIGNER_ADDR>,bc1p..." \
--protostones "[32,0,77]:v1:v1" \
-y

How Unwrapping Works

Unwrapping converts frBTC back to native BTC in a two-step process:

Step 1: Burn frBTC

Create a transaction that burns your frBTC and queues a payment request:

┌─────────────────────────────────────────────────────────┐
│ UNWRAP TRANSACTION │
├─────────────────────────────────────────────────────────┤
│ Input: Your frBTC UTXO │
├─────────────────────────────────────────────────────────┤
│ OP_RETURN: Protostone calling unwrap() opcode 78 │
│ with vout and amount parameters │
└─────────────────────────────────────────────────────────┘

Step 2: Receive BTC

The SUBFROST signers monitor for unwrap requests and cooperatively sign a Bitcoin transaction to release the locked BTC to your specified address.

Minimum Unwrap Amount

Due to dust limits and transaction fees, there's a minimum unwrap amount:

alkanes subfrost minimum-unwrap \
--fee-rate <SAT_PER_VB> \
--premium 0.001

The formula considers:

  • Dust threshold (546 sats)
  • Fee per output based on transaction size
  • Protocol premium (default 0.1%)

Transaction Structure

Protostone Format

Wrap transactions include a protostone in the OP_RETURN:

Protostone {
cellpack: {
target: {block: 32, tx: 0}, // frBTC contract
inputs: [77] // exchange() opcode
},
bitcoin_transfer: {
amount: <SATS>,
target: Output(0) // Send BTC to output 0
},
pointer: Output(1), // frBTC goes to output 1
refund: Output(1) // Unused tokens back to output 1
}

Payment Storage

Unwrap payments are stored by block height:

/alkanes/{32:0}/storage/
/payments/byheight/{height} // Payments created at block
/fulfilled // Fulfilled payment tracking
/last_block // Last processed block

Checking Balances

frBTC Balance

alkanes alkanes getbalance --address <YOUR_ADDRESS>

Pending Unwraps

View pending unwrap requests:

alkanes alkanes unwrap --block-tag latest

Security Model

Atomic Wrapping

  • Wrapping is trustless and permissionless
  • BTC lock and frBTC mint happen in the same transaction
  • If the BTC transfer fails, no frBTC is minted

Trust-Minimized Unwrapping

  • Unwrapping requires SUBFROST signer cooperation
  • Signers monitor for unwrap events
  • Multi-party threshold signature required for BTC release
  • Users can verify pending payments on-chain

Error Handling

"Insufficient funds"

  • Ensure you have enough BTC for the wrap amount plus transaction fees
  • Check that your UTXOs aren't locked or already spent

"Below minimum unwrap"

  • Your unwrap amount is below the dust threshold plus fees
  • Use minimum-unwrap command to check the current minimum

"Signer address mismatch"

  • The BTC was sent to the wrong address
  • Query the current signer address before wrapping

Complete Example

Here's a complete wrap flow:

# 1. Create wallet if needed
alkanes wallet create

# 2. Check your addresses
alkanes wallet addresses --range 0:3

# 3. Fund your wallet with BTC (external step)

# 4. Check balance
alkanes wallet balance

# 5. Wrap 0.01 BTC
alkanes alkanes wrap-btc 0.01 \
--to bc1p... \
--from bc1q... \
--change bc1q... \
--fee-rate 10 \
-y

# 6. Verify frBTC balance
alkanes alkanes getbalance --address bc1p...

Key Parameters

ParameterValueDescription
frBTC Contract{32, 0}Block 32, Transaction 0
Wrap Opcode77exchange() function
Unwrap Opcode78unwrap() function
Default Premium0.1%Protocol fee on unwrap
Dust Threshold546 satsMinimum output value

See Also