Fetch testnet Bitcoin on regtest
Request testnet BTC from the Hiro faucet for local development and testing
const TESTNET_ADDRESS = 'bcrt1q728h29ejjttmkupwdkyu2x4zcmkuc3q29gvwaa';try {const response = await fetch(`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${TESTNET_ADDRESS}`,{method: 'POST',headers: {"Content-Type": "application/json",},});const result = await response.json();console.log("Faucet response:", result);if (result.success) {console.log(`Transaction ID: ${result.txid}`);console.log(`Amount sent: ${result.amount} sats`);}} catch (error) {console.error("Faucet request failed:", error);}
Use cases
- Local development with Bitcoin transactions
- Testing sBTC operations
- Integration testing for cross-chain applications
- Developing Bitcoin-aware smart contracts
Key concepts
The Hiro testnet faucet:
- Rate limited: One request per address per hour
- Amount: Sends 0.5 testnet BTC per request
- Network: Works with Bitcoin testnet/regtest addresses
- Format: Supports legacy, segwit, and taproot addresses
Generate a testnet address
import * as bitcoin from 'bitcoinjs-lib';import * as ecc from 'tiny-secp256k1';// Initialize ECC librarybitcoin.initEccLib(ecc);// Define testnet networkconst testnet = bitcoin.networks.testnet;// Generate new key pairconst keyPair = bitcoin.ECPair.makeRandom({ network: testnet });// Generate different address typesconst { address: p2wpkh } = bitcoin.payments.p2wpkh({pubkey: keyPair.publicKey,network: testnet});const { address: p2tr } = bitcoin.payments.p2tr({pubkey: keyPair.publicKey.slice(1),network: testnet});console.log("Segwit address:", p2wpkh); // tb1q...console.log("Taproot address:", p2tr); // tb1p...
Request with retry logic
async function requestBTCWithRetry(address: string,maxRetries = 3): Promise<any> {const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));for (let i = 0; i < maxRetries; i++) {try {const response = await fetch(`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${address}`,{method: 'POST',headers: { 'Content-Type': 'application/json' },});if (response.ok) {return await response.json();}if (response.status === 429) {const retryAfter = response.headers.get('Retry-After');console.log(`Rate limited. Retry after ${retryAfter}s`);await delay(parseInt(retryAfter || '3600') * 1000);continue;}throw new Error(`HTTP ${response.status}: ${response.statusText}`);} catch (error) {if (i === maxRetries - 1) throw error;console.log(`Attempt ${i + 1} failed, retrying...`);await delay(2000 * (i + 1)); // Exponential backoff}}}
Monitor transaction confirmation
async function waitForConfirmation(txid: string): Promise<void> {console.log(`Waiting for transaction ${txid} to confirm...`);while (true) {try {const response = await fetch(`https://api.testnet.hiro.so/extended/v1/tx/${txid}`);if (response.ok) {const tx = await response.json();if (tx.tx_status === 'success') {console.log(`Transaction confirmed in block ${tx.block_height}`);break;}}// Wait 10 seconds before checking againawait new Promise(resolve => setTimeout(resolve, 10000));} catch (error) {console.error("Error checking transaction:", error);}}}// Usageconst result = await requestBTCWithRetry(TESTNET_ADDRESS);if (result.success) {await waitForConfirmation(result.txid);}
Local development
For local regtest environments, you'll need to run your own Bitcoin node and mine blocks. The Hiro faucet only works with the public testnet.
Setting up local regtest
# Start Bitcoin regtest nodebitcoind -regtest -rpcuser=user -rpcpassword=pass# Generate new addressbitcoin-cli -regtest getnewaddress# Mine blocks to that addressbitcoin-cli -regtest generatetoaddress 101 <address># Send BTC to your test addressbitcoin-cli -regtest sendtoaddress <test-address> 1
Integration with sBTC
// Request BTC for sBTC testingasync function prepareSBTCTest(btcAddress: string, stxAddress: string) {// 1. Request testnet BTCconst btcResult = await fetch(`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${btcAddress}`,{ method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(r => r.json());// 2. Request testnet STX for feesconst stxResult = await fetch(`https://api.testnet.hiro.so/extended/v1/faucets/stx?address=${stxAddress}`,{ method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(r => r.json());return {btcTxid: btcResult.txid,stxTxid: stxResult.txid,btcAmount: btcResult.amount,stxAmount: stxResult.amount};}