For any Catalog-related blockchain interactions, in Golang.
- Bitcoin
- Ethereum
The blockchain
package can be imported by running:
$ go get github.com/catalogfi/blockchain
This client follows the standard bitcoind JSON-RPC interface.
Example:
// Initialize the client.
config := &rpcclient.ConnConfig{
Params: chaincfg.RegressionNetParams.Name,
Host: "0.0.0.0:18443",
User: user,
Pass: password,
HTTPPostMode: true,
DisableTLS: true,
}
client := btc.NewClient(config)
// Get the latest block.
height, hash, err := client.LatestBlock()
if err != nil {
panic(err)
}
The indexer client follows the electrs indexer API.
Example:
// Initialize the client.
logger, _ := zap.NewDevelopment()
indexer := btc.NewElectrsIndexerClient(logger, host, btc.DefaultRetryInterval)
addr := "xxxxxxx"
utxos, err := indexer.GetUTXOs(context.Background(), addr)
if err != nil {
panic(err)
}
There are a few commonly used fee estimators to give you an estimate of the current network. Beware, these are mostly for mainnet, as min relay fees usually is enough for testnet and regnet.
The result is in
sats/vB
Example:
// Mempool uses the mempool's public fee estimation api (https://mempool.space/docs/api/rest#get-recommended-fees).
// It returns currently suggested fees for new transactions. It's safe for concurrent use and you can define a
// duration for how long you want to cache the result to avoid spamming the requests.
estimator := btc.NewMempoolFeeEstimator(&chaincfg.MainNetParams, btc.MempoolFeeAPI, 15*time.Second)
fees, err := estimator.FeeSuggestion()
if err != nil {
panic(err)
}
// Blockstream uses the blockstream api and returns a fee estimation basing on the past blocks.
estimator := btc.NewBlockstreamFeeEstimator(&chaincfg.MainNetParams, btc.BlockstreamAPI, 15*time.Second)
fees, err := estimator.FeeSuggestion()
if err != nil {
panic(err)
}
// If you know the exact fee rate you want, you can use the FixedFeeEstimator which will always return the provided
// fee rate.
estimator := btc.NewFixedFeeEstimator(10)
fees, err := estimator.FeeSuggestion()
if err != nil {
panic(err)
}
One important use case for this library is building a Bitcoin transaction. We typically use the BuildTransaction function for this purpose. Although this function has many parameters, they can be grouped into three categories:
- General
network:
The network on which the transaction is built.feeRate:
The minimum fee rate for the transaction. The actual transaction fee might be slightly higher due to estimation using the upper bound.
- Inputs
inputs:
The UTXOs (unspent transaction outputs) you want to spend in the transaction. These are guaranteed to be included. Provide the estimated size of these UTXOs. Use the default valueNewRawInputs()
if you don't have any specific UTXOs.utxos:
Available UTXOs that can be added to the inputs if their amount is insufficient to cover the output. The UTXOs are added in the order provided. Usenil
if you don't have any.sizeUpdater:
Describes how much size each UTXO adds to the transaction. It assumes all UTXOs come from the same address. Predefined size updaters likeP2pkhUpdater
andP2wpkhUpdater
are available. Usenil
ifutxos
is empty.
- Outputs
recipients:
Specifies who will receive the funds. All recipients are guaranteed to receive the specified amount.changeAddr:
The address where you want to send the change, typically the sender's address.
Examples:
- Transfer 0.1 btc to
1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
// Fetch available utxos of the address
utxos, err := indexer.GetUTXOs(ctx, sender)
if err != nil {
panic(err)
}
recipients := []btc.Recipient{
{
To: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
Amount: 1e7,
},
}
transaction, err := btc.BuildTransaction(&chaincfg.MainNetParams, 20, btc.NewRawInputs(), utxos, btc.P2pkhUpdater, recipients, sender)
Expect(err).To(BeNil())
- Spend a UTXO and send all the money to
1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
rawInputs := btc.RawInputs{
VIN: utxos,
BaseSize: txsizes.RedeemP2PKHSigScriptSize * len(utxos),
SegwitSize: 0,
}
sender := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
transaction, err := btc.BuildTransaction(&chaincfg.MainNetParams, 20, rawInputs, nil, nil, nil, sender)
Expect(err).To(BeNil())
- Redeem an HTLC and send 0.1 btc to
1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
and rest to sender
htlcUtxos := []btc.UTXO{
{
TxID: txid,
Vout: vout,
Amount: amount,
},
}
rawInputs := btc.RawInputs{
VIN: htlcUtxos,
BaseSize: 0,
SegwitSize: btc.RedeemHtlcRefundSigScriptSize,
}
recipients := []btc.Recipient{
{
To: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
Amount: 1e7,
},
}
transaction, err := btc.BuildTransaction(&chaincfg.MainNetParams, 20, rawInputs, nil, nil, recipients, sender)
Expect(err).To(BeNil())