Skip to main content

Running a BTC Staker for a Cosmos Chain

This guide explains how to set up and run a BTC staker for a Cosmos chain integrated with Babylon.

Prerequisites

Bitcoin

You can compile bitcoin v26.x from source, or install one of the pre-built binary images for your OS.

BTC Staker

To get started, clone the repository to your local machine from GitHub:

git clone https://github.com/babylonlabs-io/btc-staker.git

Then, check out the v1.99.0-devnet.6 tag:

cd btc-staker
git checkout v1.99.0-devnet.6

At the top-level directory of the project:

make install

The above command will build and install the following binaries to $GOPATH/bin:

  • stakerd: The daemon program for the btc-staker
  • stakercli: The CLI tool for interacting with stakerd.

Getting Signet BTC Tokens

The Euphrates devnet is connected to BTC Signet. There's information on how to connect to Signet in the BTC Signet wiki. After creating a wallet and a new BTC address in the signet network, you can visit Bitcoin's signet faucets (e.g., https://signetfaucet.com) for signet BTC. Babylon also provides a BTC signet faucet in the Babylon discord server.

Setting Up the BTC Staker Daemon

To stake BTC to Finality Providers, you need to set up a BTC Staker daemon that generates Bitcoin staking transactions and sends staking requests to Babylon.

Configuration File

Create a configuration file for the BTC staker daemon. Below is an example configuration file (stakerd.conf):

[Application Options]
; Logging level for all subsystems {trace, debug, info, warn, error, fatal}
DebugLevel = debug
; The base directory that contains staker's data, logs, configuration file, etc.
StakerdDir = /home/btcstaker/.stakerd
; Path to configuration file
ConfigFile = /home/btcstaker/.stakerd/stakerd.conf
; The directory to store staker's data within
DataDir = /home/btcstaker/.stakerd/data
; Directory to log output.
LogDir = /home/btcstaker/.stakerd/logs
; Write CPU profile to the specified file
CPUProfile =
; Enable HTTP profiling on either a port or host:port
Profile =
; If config filr does not exist, create it with current settings
DumpCfg = false
; Add an interface/port/socket to listen for RPC connections
RawRPCListeners = 0.0.0.0:15812
[walletconfig]
; name of the wallet to sign Bitcoin transactions
WalletName = btcstaker
; passphrase to unlock the wallet
WalletPass = walletpass
[stakerconfig]
; The interval for Babylon node BTC light client to catch up with the real chain before re-sending delegation request
BabylonStallingInterval = 20s
; The interval for staker to check whether unbonding tx received finality-provider and covenant signatures
UnbondingTxCheckInterval = 20s
; Exit stakerd on critical error
ExitOnCriticalError = true
[walletrpcconfig]
; location of the wallet rpc server
Host = bitcoindsim:18443/wallet/btcstaker
; user auth for the wallet rpc server
User = rpcuser
; password auth for the wallet rpc server
Pass = rpcpass
; disables tls for the wallet rpc client
DisableTls = true
[chain]
; network to run on
Network = signet
; Connect to a custom signet network defined by this challenge instead of using the global default signet test network -- Can be specified multiple times
SigNetChallenge =
[btcnodebackend]
; type of node to connect to {bitcoind, btcd}
Nodetype = bitcoind
; type of wallet to connect to {bitcoind, btcwallet}
WalletType = bitcoind
; fee mode to use for fee estimation {static, dynamic}. In dynamic mode fee will be estimated using backend node
FeeMode = static
; minimum fee rate to use for fee estimation in sat/vbyte. If fee estimation by connected btc node returns a lower fee rate, this value will be used instead
MinFeeRate = 2
; maximum fee rate to use for fee estimation in sat/vbyte. If fee estimation by connected btc node returns a higher fee rate, this value will be used instead. It is also used as fallback if fee estimation by connected btc node fails and as fee rate in case of static estimator
MaxFeeRate = 25
[btcd]
; The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used.
RPCHost = 127.0.0.1:18334
; Username for RPC connections
RPCUser = user
; Password for RPC connections
RPCPass = pass
; File containing the daemon's certificate file
RPCCert = /Users/vitsalis/Library/Application Support/Btcd/rpc.cert
; The raw bytes of the daemon's PEM-encoded certificate chain which will be used to authenticate the RPC connection.
RawRPCCert =
; size of the Bitcoin blocks cache
BlockCacheSize = 100000
[bitcoind]
; The daemon's rpc listening address
RPCHost = bitcoindsim:18443
; Username for RPC connections
RPCUser = rpcuser
; Password for RPC connections
RPCPass = rpcpass
; The address listening for ZMQ connections to deliver raw block notifications
ZMQPubRawBlock = tcp://bitcoindsim:29001
; The address listening for ZMQ connections to deliver raw transaction notifications
ZMQPubRawTx = tcp://bitcoindsim:29002
; The read deadline for reading ZMQ messages from both the block and tx subscriptions
ZMQReadDeadline = 30s
; The fee estimate mode. Must be either ECONOMICAL or CONSERVATIVE.
EstimateMode = CONSERVATIVE
; The maximum number of peers staker will choose from the backend node to retrieve pruned blocks from. This only applies to pruned nodes.
PrunedNodeMaxPeers = 0
; Poll the bitcoind RPC interface for block and transaction notifications instead of using the ZMQ interface
RPCPolling = false
; The interval that will be used to poll bitcoind for new blocks. Only used if rpcpolling is true.
BlockPollingInterval = 30s
; The interval that will be used to poll bitcoind for new tx. Only used if rpcpolling is true.
TxPollingInterval = 30s
; size of the Bitcoin blocks cache
BlockCacheSize = 1000000
[babylon]
; name of the key to sign transactions with
Key = btc-staker
; chain id of the chain to connect to
ChainID = euphrates-0.6.0
; address of the rpc server to connect to
RPCAddr = https://rpc-euphrates.devnet.babylonlabs.io:443
; address of the grpc server to connect to
GRPCAddr = https://rpc-euphrates.devnet.babylonlabs.io:9090
; account prefix to use for addresses
AccountPrefix = bbn
; type of keyring to use
KeyringBackend = test
; adjustment factor when using gas estimation
GasAdjustment = 1.5
; comma separated minimum gas prices to accept for transactions
GasPrices = 0.01ubbn
; directory to store keys in
KeyDirectory = /home/btcstaker/.stakerd
; flag to print debug output
Debug = true
; client timeout when doing queries
Timeout = 20s
; block timeout when waiting for block events
BlockTimeout = 1m0s
; default output when printint responses
OutputFormat = json
; sign mode to use
SignModeStr = direct
[dbconfig]
; The directory path in which the database file should be stored.
DBPath = /home/btcstaker/.stakerd/data
; The name of the database file.
DBFileName = staker.db
; Prevents the database from syncing its freelist to disk, resulting in improved performance at the expense of increased startup time.
NoFreelistSync = true
; Specifies if a Bolt based database backend should be automatically compacted on startup (if the minimum age of the database file is reached). This will require additional disk space for the compacted copy of the database but will result in an overall lower database size after the compaction.
AutoCompact = false
; Specifies the minimum time that must have passed since a bolt database file was last compacted for the compaction to be considered again.
AutoCompactMinAge = 168h0m0s
; Specifies the timeout value to use when opening the wallet database.
DBTimeout = 1m0s

Adjust this configuration according to your setup, particularly the paths and connection details for Bitcoin and Babylon.

Staking BTC to Finality Providers

After setting up the BTC staker daemon, you can stake BTC to Finality Providers.

List Finality Providers

First, you can use the following command to list all Babylon Finality Providers:

$ stakercli daemon babylon-finality-providers

or

$ babylond query btcstaking finality-provider <fp_pk> --node $nodeUrl

List Available BTC Outputs

Find the BTC address that has sufficient Bitcoin balance that you want to stake from:

$ stakercli daemon list-outputs

Stake Bitcoin

Stake Bitcoin to the Finality Provider of your choice. The --staking-time flag specifies the timelock of the staking transaction in BTC blocks. The --staking-amount flag specifies the amount in Satoshis to stake.

$ stakercli daemon stake \
--staker-address <staker_btc_address> \
--staking-amount 1000000 \
--finality-providers-pks <babylon_fp_pk> \
--finality-providers-pks <BSN_fp_pk> ... \
--staking-time 10000 # ~70 days

Note that among public keys specified in --finality-providers-pks, at least one of them should be a Babylon Finality Provider. For example, you can let the BTC delegation to restake to a Babylon Finality Provider as well as a Finality Provider for your BSN.

Verify BTC Delegation

You can query the Babylon node to see the BTC delegation:

$ babylond query btcstaking btc-delegations any --node https://rpc-euphrates.devnet.babylonlabs.io

This will show all the BTC delegations including yours.

Reward Distribution

The Babylon contracts distribute rewards to BTC stakers upon each EndBlock.

You can query the reward distribution via:

$ babylond query incentive reward-gauges <your-bech32-address> # of BTC staker 

To withdraw the reward, you can use the following command if you are a BTC staker:

$ babylond tx incentive withdraw-reward btc_delegation --from <your-bech32-address>