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:
objectUTXOPrepare 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:
objectAccumulativeResult 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