sBTC integration with Clarinet
Build and test smart contracts that use sBTC, the decentralized Bitcoin-backed token on Stacks.
What you'll learn
In this guide, you'll learn how to interact with the mainnet sBTC contract in your Clarinet project.
Prerequisites
To follow this quickstart guide, you'll need:
Quickstart
Add sBTC to your project
Add the sBTC smart contracts to your Clarinet project requirements:
$clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit
This command adds three essential contracts:
sbtc-token
- The core SIP-010 token contractsbtc-registry
- Registry for managing sBTC configurationsbtc-deposit
- Handles deposit and withdrawal operations
When Clarinet detects these contracts, it automatically funds your test wallets with sBTC for testing.
Create an sBTC-enabled contract
Build a simple NFT marketplace that accepts sBTC payments:
;; Define NFT(define-non-fungible-token marketplace-nft uint);; Price in sats (smallest sBTC unit)(define-data-var mint-price uint u100)(define-data-var next-id uint u0);; Mint NFT with sBTC payment(define-public (mint-with-sbtc)(begin;; Transfer sBTC from buyer to contract(try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token transfer(var-get mint-price)tx-sender(as-contract tx-sender)none));; Mint the NFT(try! (nft-mint? marketplace-nft (var-get next-id) tx-sender));; Increment ID for next mint(ok (var-set next-id (+ (var-get next-id) u1)))));; Check sBTC balance(define-read-only (get-sbtc-balance (owner principal))(contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token get-balance owner))
Test in Clarinet console
Clarinet automatically funds test wallets with sBTC. Test your contract:
$clarinet console
Check wallet balances and mint an NFT:
;; Check deployer's sBTC balance (auto-funded)(contract-call? .nft-marketplace get-sbtc-balance tx-sender);; Mint NFT with wallet_1 (also auto-funded)(contract-call? .nft-marketplace mint-with-sbtc);; Verify NFT ownership(nft-get-owner? .nft-marketplace marketplace-nft u0)
Write unit tests
Test sBTC functionality in your TypeScript tests:
import { describe, expect, it } from "vitest";import { Cl } from "@stacks/transactions";describe("NFT Marketplace", () => {it("mints NFT with sBTC payment", () => {const mintPrice = 100;// Get initial sBTC balanceconst initialBalance = simnet.callReadOnlyFn("nft-marketplace","get-sbtc-balance",[Cl.standardPrincipal(accounts.get("wallet_1")!.address)],accounts.get("wallet_1")!.address);// Mint NFTconst mintResult = simnet.callPublicFn("nft-marketplace","mint-with-sbtc",[],accounts.get("wallet_1")!.address);expect(mintResult.result).toBeOk();// Verify sBTC was transferredconst finalBalance = simnet.callReadOnlyFn("nft-marketplace","get-sbtc-balance",[Cl.standardPrincipal(accounts.get("wallet_1")!.address)],accounts.get("wallet_1")!.address);expect(Number(Cl.parse(finalBalance.result))).toBeLessThan(Number(Cl.parse(initialBalance.result)));});});
Deploy to testnet
On testnet, Clarinet automatically remaps to the official Hiro sBTC contracts:
$clarinet deployments generate --testnet
Your deployment plan shows the remapped addresses:
---id: 0name: Testnet deploymentnetwork: testnetstacks-node: "https://api.testnet.hiro.so"bitcoin-node: "http://blockstream.info"plan:batches:- id: 0transactions:- requirement-publish:contract-id: ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-tokenremap-sender: SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4remap-principals:SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4: ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXTcost: 50000path: "./.cache/requirements/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token.clar"
Deploy to testnet:
$clarinet deployments apply -p deployments/default.testnet-plan.yaml
Common patterns
Working with sBTC addresses
Clarinet handles sBTC contract address mapping across networks:
Network | sBTC Contract Address |
---|---|
Simnet/Devnet | SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token |
Testnet | ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token |
Mainnet | Contract address remains unchanged |
Your contract code always references the simnet address. Clarinet automatically remaps during deployment.
Manual sBTC minting in unit tests
While Clarinet 2.15.0+ automatically funds wallets with sBTC in devnet, you may need to manually mint sBTC in unit tests for specific scenarios:
Minting sBTC using the deployer address
The sBTC token contract allows the deployer (multisig) address to mint tokens. Use this approach in your tests:
import { describe, expect, it } from "vitest";import { Cl } from "@stacks/transactions";describe("Manual sBTC minting", () => {it("mints sBTC to custom addresses", () => {// The sBTC multisig address that can mintconst sbtcDeployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4";const customWallet = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";// Mint 1000 sats to custom walletconst mintResult = simnet.callPublicFn("SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token","mint",[Cl.uint(1000), // amount in satsCl.principal(customWallet) // recipient],sbtcDeployer // sender must be deployer);expect(mintResult.result).toBeOk(Cl.bool(true));// Verify balanceconst balance = simnet.callReadOnlyFn("SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token","get-balance",[Cl.principal(customWallet)],customWallet);expect(balance.result).toBeOk(Cl.uint(1000));});});
Testing with mainnet execution simulation
When using mainnet execution simulation, you can mint sBTC using the actual mainnet multisig:
// Enable mainnet simulation in Clarinet.tomlconst mainnetMultisig = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4";const mainnetWallet = "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR";// Mint sBTC to any mainnet addresssimnet.callPublicFn("SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token","mint",[Cl.uint(100000), Cl.principal(mainnetWallet)],mainnetMultisig);
This approach is useful for:
- Testing specific sBTC amounts
- Simulating different wallet balances
- Testing edge cases with precise token amounts
- Integration testing with mainnet contracts
Next steps
- Learn about SIP-010 fungible token standard
- Explore Stacks.js integration for sBTC transactions
- Set up Chainhook monitoring for sBTC events