Bitcoin client

This package provides a client to interact with Bitcoin blockchain. It supports the following features:

  • Get address, private key and public key

  • Get balance of an address

  • Get transaction details

  • Get transactions by address

  • Get unspent transaction outputs

  • Get transaction fee

  • Prepare and send transaction

The underlying implementation uses bitcoinlib library. https://bitcoinlib.readthedocs.io/en/latest/

We use the function accumulative from coinselect ported to python to calculate the fee and select the inputs and outputs of the transaction. See: https://github.com/bitcoinjs/coinselect

Quick Start

This example shows how to create a BTC client and get the balance of an address. After that, it shows how to send a transaction between two addresses (0 index and 1 index of the mnemonic phrase).

import asyncio
import getpass

from xchainpy2_bitcoin import BitcoinClient
from xchainpy2_utils import NetworkType


async def send_btc():
    phrase = getpass.getpass('Enter a BIP39 mnemonic phrase: ')

    btc = BitcoinClient(phrase=phrase, network=NetworkType.TESTNET)
    btc2 = BitcoinClient(phrase=phrase, network=NetworkType.TESTNET, wallet_index=1)

    available_providers = btc.get_available_provider_names()
    print(f"Available providers: {available_providers}")

    source_address = btc.get_address()
    dest_address = btc2.get_address()

    print(f"First BTC Address: {source_address}")
    print(f"Second BTC Address: {dest_address}")

    balance1 = await btc.get_balance()
    print(f"Balance 1 ({source_address}): {balance1}")

    balance2 = await btc2.get_balance()
    print(f"Balance 2 ({dest_address}): {balance2}")

    if balance2 > balance1:
        print('Swapping addresses')
        btc, btc2 = btc2, btc

    tx_hash = await btc.transfer(btc2.gas_amount(0.00001234), dest_address, memo='test')

    print(f"Transfer hash: {tx_hash} ({btc2.get_explorer_tx_url(tx_hash)})")

    print("Waiting for transaction to complete (10 minutes)...")
    tx = await btc.wait_for_transaction(tx_hash)
    print(f"Transaction confirmed: {tx}")

    balance = await btc.get_balance()
    print(f"Balance 1 ({source_address}): {balance}")

    balance = await btc2.get_balance()
    print(f"Balance 2 ({dest_address}): {balance}")


async def main():
    await send_btc()


if __name__ == '__main__':
    asyncio.run(main())

BTC Client class

class xchainpy2_bitcoin.btc_client.BitcoinClient(network=NetworkType.MAINNET, phrase: str | None = None, private_key: str | bytes | callable | None = None, root_derivation_paths: Dict[NetworkType, str] | None = {NetworkType.DEVNET: "84'/1'/0'/0/", NetworkType.MAINNET: "84'/0'/0'/0/", NetworkType.STAGENET: "84'/0'/0'/0/", NetworkType.TESTNET: "84'/1'/0'/0/"}, explorer_providers={NetworkType.MAINNET: ('https://blockstream.info/', 'https://blockstream.info/address/{address}', 'https://blockstream.info/tx/{tx_id}'), NetworkType.STAGENET: ('https://blockstream.info/', 'https://blockstream.info/address/{address}', 'https://blockstream.info/tx/{tx_id}'), NetworkType.TESTNET: ('https://blockstream.info/testnet/', 'https://blockstream.info/testnet/address/{address}', 'https://blockstream.info/testnet/tx/{tx_id}')}, wallet_index=0, provider_names=None, daemon_url=None, _chain=Chain.Bitcoin, service_kwargs=None)

Bases: XChainClient

async broadcast_tx(tx_hex: str) str

Broadcast the transaction to the network.

Parameters:

tx_hex – Signed transaction in hex format string

Returns:

Transaction ID (txid)

get_address() str

Get the address of the wallet Bech32 encoding is used to get the address from the public key.

Returns:

address

get_available_provider_names(network_name=None)

Get available provider names for the network.

Parameters:

network_name – network name like “bitcoin”, “dogecoin”, etc. (default is the current client’s network)

Returns:

set of provider names

classmethod get_available_providers_g(network_name=None)

Get available providers for the network. This is a class method.

Parameters:

network_name – network name like “bitcoin”, “dogecoin”, etc. (default is None)

Returns:

set of provider names

async get_balance(address: str = '', **kwargs) List[CryptoAmount]

Get the BTC balance of the wallet.

Parameters:

address – address (default is the address of the wallet)

Returns:

list of CryptoAmount, containing a single CryptoAmount with the balance

async get_fees(average_blocks=10, fast_blocks=3, fastest_blocks=1) FeeProgressive

Get the fee rates triplet (average, fast, fastest) in satoshi per byte.

Parameters:
  • average_blocks – Number of blocks to confirm in average case

  • fast_blocks – Number of blocks to confirm in fast case

  • fastest_blocks – Number of blocks to confirm in the fastest case

Returns:

FeeProgressive object

get_private_key() Key

Get private key object. Key class is declared in bitcoinlib.

Returns:

private key object

get_public_key() Key

Get public key object of bitcoinlib

Returns:

Key

async get_transaction_data(tx_id: str) XcTx | None

Get the transaction data of the given transaction ID (txid, hash).

Parameters:

tx_id – Transaction ID (txid, hash)

Returns:

XcTx object or None if the transaction is not found

async get_transactions(address: str = '', offset: int = 0, limit: int = 10, start_time: datetime | None = None, end_time: datetime | None = None, asset: Asset | None = None, height=None, detailed=False, after_tx_id='') TxPage

Get the recent transactions of the wallet.

Parameters:
  • address – Address of the wallet

  • offset – Offset from the beginning of the list

  • limit – Count of transactions to return

  • start_time – (not supported)

  • end_time – (not supported)

  • asset – (not supported)

  • height – (not supported)

  • detailed – (not supported)

  • after_tx_id – Transaction ID to start listing from

Returns:

TxPage object

async get_utxos(address='', limit=20, full=False) List[UTXO]

Get UTXOs of the wallet. UTxO is an unspent transaction output. If you request full data, it will iterate over all UTXOs and get the full transaction data (much slower).

Parameters:
  • address – address (default is the address of the wallet)

  • limit – maximum number of UTXOs to return

  • full – if True, returns full transaction data

Returns:

list of UTXOs

recreate_service_with_providers(provider_names)

Recreate the service with the specified provider names.

Parameters:

provider_names – a list of provider names

async transfer(what: CryptoAmount, recipient: str, memo: str | None = None, gas: Gas | None = None, check_balance: bool = True, min_confirmations=1, **kwargs) str

Transfer UTXO gas asset (BTC eg) to recipient. Memo is optional and encoded in OP_RETURN output.

Parameters:
  • what – amount to transfer, must be gas asset (BTC.BTC)

  • recipient – recipient address

  • gas – Gas options

  • memo – optional memo

  • check_balance – if True, checks the balance of the wallet before transfer

  • min_confirmations – minimum confirmations

Returns:

transaction id (txid)

validate_address(address: str) bool

Validate BTC address

Parameters:

address – address to validate

Returns:

bool

Constants

xchainpy2_bitcoin.const.AssetTestBTC = ('BTC', 'TBTC', '', AssetKind.NATIVE)

Asset for Bitcoin.

xchainpy2_bitcoin.const.BLOCKSTREAM_EXPLORERS = {NetworkType.MAINNET: ('https://blockstream.info/', 'https://blockstream.info/address/{address}', 'https://blockstream.info/tx/{tx_id}'), NetworkType.STAGENET: ('https://blockstream.info/', 'https://blockstream.info/address/{address}', 'https://blockstream.info/tx/{tx_id}'), NetworkType.TESTNET: ('https://blockstream.info/testnet/', 'https://blockstream.info/testnet/address/{address}', 'https://blockstream.info/testnet/tx/{tx_id}')}

Maximum memo length for Bitcoin. Please refer to the following link for more information about memo length reduction: https://dev.thorchain.org/concepts/memo-length-reduction.html

xchainpy2_bitcoin.const.BTC_BLOCKCHAIR_MAINNET_EXPLORER = ('https://blockchair.com/', 'https://blockchair.com/bitcoin/address/{address}', 'https://blockchair.com/bitcoin/transaction/{tx_id}')

Explorer providers for Bitcoin (Blockchair testnet).

xchainpy2_bitcoin.const.BTC_BLOCKCHAIR_TESTNET_EXPLORER = ('https://blockchair.com/', 'https://blockchair.com/bitcoin/testnet/address/{address}', 'https://blockchair.com/bitcoin/testnet/transaction/{tx_id}')

Explorer providers for Bitcoin (Blockcypher mainnet).

xchainpy2_bitcoin.const.BTC_BLOCKSTREAM_MAINNET_EXPLORER = ('https://blockstream.info/', 'https://blockstream.info/address/{address}', 'https://blockstream.info/tx/{tx_id}')

Explorer providers for Bitcoin (Blockstream testnet).

xchainpy2_bitcoin.const.BTC_BLOCKSTREAM_TESTNET_EXPLORER = ('https://blockstream.info/testnet/', 'https://blockstream.info/testnet/address/{address}', 'https://blockstream.info/testnet/tx/{tx_id}')

Explorer providers for Bitcoin (Blockchair mainnet).

xchainpy2_bitcoin.const.BTC_DECIMAL = 8

BIP44 derivation path for Bitcoin mainnet wallets.

This path is used for deriving keys according to BIP44 standard for compatibility with older Bitcoin wallets. These are non-segwit addresses. These addresses always begin with a 1.

xchainpy2_bitcoin.const.BTC_DEFAULT_FEE_BOUNDS = (1, 1000)

Unicode symbols for Bitcoin.

xchainpy2_bitcoin.const.BTC_SATOSHI_SYMBOL = '⚡'

Explorer providers for Bitcoin (Blockstream mainnet).

xchainpy2_bitcoin.const.BTC_SYMBOL = '₿'

Unicode symbol for Satoshi.

xchainpy2_bitcoin.const.FOO = ('BTC', 'TBTC', '', AssetKind.NATIVE)

FOO

xchainpy2_bitcoin.const.MAX_MEMO_LENGTH = 80

Default Bitcoin provider that are used to broadcast transactions and get transaction data.

xchainpy2_bitcoin.const.ROOT_DERIVATION_PATHS = {NetworkType.DEVNET: "84'/1'/0'/0/", NetworkType.MAINNET: "84'/0'/0'/0/", NetworkType.STAGENET: "84'/0'/0'/0/", NetworkType.TESTNET: "84'/1'/0'/0/"}

Default fee bounds for Bitcoin transactions. The minimum fee is set to 1 sat/byte and the maximum fee is set to 1000 sat/byte. Fees which are higher than this are considered to be too high and are rejected.

xchainpy2_bitcoin.const.ROOT_DERIVATION_PATH_44 = "44'/0'/0'/0/"

BIP49 derivation path for Bitcoin mainnet wallets.

BIP49 refers to the accepted common standard of deriving segwit “compatibility” addresses. These addresses begin with a 3.

xchainpy2_bitcoin.const.ROOT_DERIVATION_PATH_49 = "49'/0'/0'/0/"

BIP84 derivation path for Bitcoin mainnet wallets.

BIP84 refers to the accepted common standard of deriving native segwit addresses. These addresses always begin with bc1 - and are referred to bech32 addresses.

xchainpy2_bitcoin.const.ROOT_DERIVATION_PATH_84 = "84'/0'/0'/0/"

BIP44 derivation path for Bitcoin testnet wallets.

xchainpy2_bitcoin.const.ROOT_DERIVATION_PATH_TEST_NET_84 = "84'/1'/0'/0/"

Dictionary of derivation paths for different networks. BIP84 is used for all networks.

xchainpy2_bitcoin.utils.AssetTestBTC

Testnet BTC asset

TX preparation functions

class xchainpy2_bitcoin.tx_prepare.UTXOPrepare(utxos: List[UTXO], service_network, fee_per_byte=1, min_confirmations=1)

Bases: object

UTXOPrepare is a class to prepare transactions in UTXO networks. It computes the inputs and outputs of a transaction based on the available UTXOs and the desired outputs.

available_utxos()

Get the available UTXOs based on the minimum confirmations.

Returns:

List of available UTXOs

build(sender: str, recipient: str, amount: Amount, memo: str | None = None) Transaction

Build a transaction based on the sender, recipient, amount and memo.

Parameters:
  • sender – Sender address

  • recipient – Address of the recipient

  • amount – Amount of gas asset to be transferred

  • memo – Optional memo to be added to the transaction

Returns:

Transaction object of bitcoinlib

property fee_rate_whole

Get the fee rate in whole number.

Returns:

Fee rate in whole number

static make_output_with_memo(memo: str)

Create an output with a memo which is OP_RETURN.

Parameters:

memo – Memo to be added to the output

Returns:

Output description

xchainpy2_bitcoin.tx_prepare.compile_memo(memo: str) bytes

Compile a memo string to a script for OP_RETURN output. https://dev.thorchain.org/concepts/sending-transactions.html#utxo-chains

Parameters:

memo (str) – The memo to be compiled

Returns:

The compiled memo: bytes

xchainpy2_bitcoin.tx_prepare.try_get_memo_from_output(out: Output) str | None

Try to get the memo string from the output script.

Parameters:

out – Output to get the memo from

Returns:

Memo string if found, None otherwise

Bitcoin Utils

xchainpy2_bitcoin.utils.get_btc_address_prefix(network: NetworkType) str

Get address prefix based on the network.

Parameters:

network – Network type

Returns:

string address prefix

Accumulative inputs selection

class xchainpy2_bitcoin.accumulative.AccumulativeResult(fee=-1, inputs=None, outputs=None)

Bases: object

AccumulativeResult is a class that represents the result of the accumulative function.

input_value(index)

Value of the input at the given index.

Parameters:

index – The index of the input

Returns:

Value of the input in satoshis

output_value(index)

Value of the output at the given index.

Parameters:

index – The index of the output

Returns:

Value of the output in satoshis

xchainpy2_bitcoin.accumulative.accumulative(utxos, outputs, fee_rate)

The Accumulative function is the main function of this module. This function selects enough inputs from the UTXOs and calculates the fee for the transaction.

Parameters:
  • utxos – The unspent transaction outputs. Each output must have a key “value” or be an integer

  • outputs – The outputs of the transaction

  • fee_rate – Fee rate in satoshis per byte

Returns:

xchainpy2_bitcoin.accumulative.dust_threshold(fee_rate)

Get the dust threshold which is fee rate times the minimal possible size of an input

Parameters:

fee_rate

Returns:

xchainpy2_bitcoin.accumulative.finalize(inputs, outputs, fee_rate)
xchainpy2_bitcoin.accumulative.get_script(item)

Get the script from the item.

Parameters:

item – An item that may contain a script

Returns:

xchainpy2_bitcoin.accumulative.get_value(v)

Get the value from the input. If the input is a dictionary, it must have a key “value”. Otherwise, the input is returned as is.

Parameters:

v – Input

Returns:

xchainpy2_bitcoin.accumulative.input_bytes(_input)

Get the number of bytes for the input.

Parameters:

_input – The input to get the number of bytes for

Returns:

xchainpy2_bitcoin.accumulative.is_finite(v)

Check if the value is finite

Parameters:

v – The value to check

Returns:

bool

xchainpy2_bitcoin.accumulative.output_bytes(output)

Get the number of bytes for the output.

Parameters:

output – The output to get the number of bytes for

Returns:

xchainpy2_bitcoin.accumulative.script_length(item)

Get the length of the script.

Parameters:

item – The item to get the length of. If the item is a dictionary, it must have a key “length”

Returns:

The length of the script

xchainpy2_bitcoin.accumulative.sum_forgiving(_range)
xchainpy2_bitcoin.accumulative.sum_or_nan(_range)
xchainpy2_bitcoin.accumulative.transaction_bytes(inputs, outputs)

Get the number of bytes for the transaction

Parameters:
  • inputs – A list of inputs

  • outputs – A list of outputs

Returns:

int

xchainpy2_bitcoin.accumulative.uint_or_nan(v)

Get the value as an unsigned integer NaN is returned if the value is not an integer, is not finite, is negative, or is not an integer.

Parameters:

v – Value to convert

Returns:

v as an unsigned integer or NaN