Require Python 3.11+
Support Python 3.13
Use uv & ruff as tooling
Breaking changes: all events received via websocket are now processed asynchronously. Ensure your handlers are accounted for concurrent access. In case not, ensure locking techniques are used (asyncio.Lock, postgres FOR UPDATE, advisory locks, etc)
We now support Python >= 3.9 only
Don't install a separate tests package, but include tests in source tarball
Electrums upgrade
Breaking changes: get_config
method no longer has default
argument, this is determined by electrum. Also it is no longer possible to programmatically disable lightning
Rename BitcartCC to Bitcart
Electrums upgrade to 4.4.5
Breaking: removed rate
and list_fiat
commands.
This is because the current exchange rates model:
- Didn't provide enough customization (tied to coingecko and it's currencies only)
- Often unreliable because sometimes depends on implementation in e.g. electrum
- Getting rate limited when many daemons are launched
To overcome the problem, we've moved the exchange rates functionality one layer up (like it should have always been), to the Merchants API
This means if you use rate command in your script, you should either use Merchants API directly (maybe your usage of SDK is not required at all), or fetch exchange rates from your favourite exchange rates provider (more customization)
Add new xpub_name
attribute in coin objects, used to display what "xpub" actually means in context of current coin (i.e. it may be xpub, or it may actually be an address)
Change license to MIT license
Upgrade electrums to 4.3.2
Add new attribute additional_xpub_fields
for coins which need it, and fix validate_key
method
Monero (XMR) support
Urgent fix: APIManager is not loading correct coin settings from anywhere when receiving events.
Classmethods load_wallet
and load_wallets
are now methods of APIManager objects.
A new parameter custom_params
was added: a mapping between currency and it's custom settings dict.
Support python 3.11
Groestlcoin (GRS) support
Tron (TRX) support
Polygon (MATIC) support
Added new is_eth_based
attribute to coin objects to ease detecting some small differences between btc and eth
We now support Python >= 3.8 only
Updates to support electrum 4.3.0
SDK no longer requires all event parameters to be present in handlers: it will detect which ones to pass by name.
I.e. if before you had:
def event_handler(event, address, status, status_str):
It can now be for example:
def event_handler(event, address):
SmartBCH support
Fix PyPi description
Added BNB (Binance Smart Chain) coin
Expose new symbol
field on Coin
objects and fix amount fields formatting in eth
Ethereum support
We now use our new universalasync package to make this library running in both sync and async contexts
Therefore get_event_loop
and idle
functions were removed. They are accessible in the universalasync
library.
Now __del__
finalizer stores a client session per event loop which should be the most correct way to handle access from many different threads and loops
Major fixes for event loop handling
More exactly, our __del__
finalizer should now work in all cases
No event loop mismatch issues are possible
Added new helper functions: get_event_loop
, which gets current event loop via the following logic:
First it tries getting a running one, then main thread one, if it's not main thread or loop was stopped (i.e. not usable), it creates a new one This function is useful as it will give a working event loop in 100% cases.
Another function added is idle
. It can be run in main thread just to block the app and make event loop running (if your code is running asynchronously in other threads).
Also our tests now test all possible usage models of the SDK, and all of them are confirmed to work without issues (even mixing threads and so on).
Drop gzro support
Migrate test suite to testnet
Ensure no unclosed session warnings are shown
Fix handling of event loop when asyncio.run
is unavailable
Properly use event loop
We now support using asyncio.run
without crashes
Better __del__
handling
Remove pin and use jsonrpcclient
4.0
Support python 3.10
Sync generator now supports calling SDK commands in any workflow, mixing threads and asyncio in any way possible: it should no longer hang!
Remove logger.exception
in favour of logger.error
Now SDK won't error out on invalid event being sent, stop websocket processing on error in handler and so on. It should be reliable now.
All exceptions are instead logged by bitcart
logger. You no longer need to put try/except in each handler to prevent accidental errors.
APIManager.load_wallet
now raises CurrencyUnsupportedError
on non-supported currency passed. It now also handles currencies in a
case-insensitive way.
Applied pre-commit normalisation of all files (whitespaces, newlines, etc), our code base is now fully formatted in an unified way.
Pins jsonrpcclient
to <4.0
to prevent breaking the library
Fixes websocket cleanup issues by mimicking asyncio.run
behaviour, it is now possible to start/stop websocket in shell
multiple times
Added new verified_tx
event support
Handle None values better in convert_amount_type
to always return Decimal.
PyPI readme fixes
Added XRG support (#25)
Added new get_invoice
method to get lightning invoices by their rhash
Drop python 3.6 support (we support 3 latest python releases)
License change to LGPLv3+
Python 3.9 support
add_invoice
now works same way as add_request
(breaking change)
This means:
message
parameter renamed todescription
- added
expire
parameter, which also defaults to 15 minutes (was 60 minutes before) amount
field in invoice data is Decimal too
Fixed APIManager websockets for multiple currencies
APIManager now supports more customization of connection options by not overriding them.
No more "no xpub provided" warnings.
APIManager now auto-reconnects properly when using multiple currencies
APIManager now calls it's handlers even when no wallet was matched
Fixed APIManager's reconnect_callback
: now currency parameter is passed to it to differentiate between calls.
New event delivery method: websockets!
Websockets should be a better way of receiving updates than webhooks.
They are better because SDK doesn't need to know it's own address, and daemon doesn't need to be notified of webhook.
That way, with less complexity, any amount of listeners per wallet are possible now.
To use it, run:
btc.start_websocket()
It will connect to websocket with auto-reconnect feature.
You can disable it by setting auto_reconnect
parameter to False
.
If force_connect
is set to True
, first time connection errors will be ignored.
By default, if connecting to websocket failed first time (might mean invalid URL), then ConnectionFailedError
is raised.
On successful connection to websocket, reconnect_callback
function is called.
If called from APIManager, currency
parameter is passed.
As webhook method is not very easy to use, it is removed in favor of websockets.
So, start_webhook
, configure_webhook
and similar related methods are removed.
Also, daemon-side all event subscription methods are removed. Now all events are sent, and are filtered by SDK.
start_websocket
in APIManager with no currencies set will now raise NoCurrenciesRegisteredError
Fixed issues with aiohttp warning and async functions in threads
All the SDK is now tested via our extensive test suite, we now gurantee to find all changes in electrum if they happen.
All the code is now following our code style.
Before, bitcart
and bitcart-async
packages existed, one providing sync API, another one async API.
Now both use cases are supported in a single bitcart
package.
You can use either:
btc.help()
Or
async def main():
await btc.help()
It is now possible to catch specific exceptions in commands, not just a general one.
from bitcart import errors
from bitcart.errors import generate_exception, ConnectionFailedError
try:
coin.help()
except ConnectionFailedError:
print("Failed connecting to daemon")
except errors.LoadingWalletError:
print("Error loading wallet")
except RequestError as e:
print(e)
New ConnectionFailedError
is raised when SDK failed to connect to daemon.
UnknownError
is raised when server returned an error code not from the spec, or spec failed loading.
RequestError
is a base error from all errors returned from server.
generate_exception function creates a new dynamic exception by passing it's name to it, used for except.
generate_exception("test") == errors.test
errors object is just a convenience wrapper around generate_exception
.
All other errors are raised and created dynamically, by the spec.
You can find your version of the spec at daemon's /spec
endpoint.
APIManager provides an easy-to-use interface to manage multiple wallets/currencies.
It is useful for tracking payments on many wallets at once, for example.
New module bitcart.utils
was added.
It has the following functions:
-
satoshis(amount: Decimal) -> int
converts btc amount to satoshis -
bitcoins(amount: int) -> Decimal
converts satoshis amount to btc -
json_encode(obj: Any) -> Any
json.dumps
supporting decimals
list_peers(gossip=False)
method will list all the lightning peers.
Now COINS
variable is available, it's a dict, where keys are strings, values are coins of respective types.
It allows introspection of available coins.
from bitcart import COINS
COINS["BTC"] # bitcart.BTC class
You can now check if two coin instances are the same like so:
if coin1 == coin2:
print("equal")
Two coin objects are equal, if their xpubs are equal, and their coin_name
is equal.
It is needed to work with latest breaking changes, see below.
SDK 1.0 is based on the latest daemon, which is using Electrum 4.0.3 for btc and other currencies, and Electron Cash 4.1.1
spec
property on coin objects and RPCProxy
objects return exceptions spec returned from daemon.
add_request
amount argument now defaults to None
, so it can be used to just query a new address, for example.
poll_updates
defaulttimeout
argument changed from 2 to 1 second- Many refactorings in the code
pay_to
andpay_to_many
functions now work without issues in concurrent environments
This update is a major version change, it means that there will be lots of breaking changes between 0.x.y -> 1.x.y series, but between 1.x.y series there should be no breaking changes. We are following semver.
It is added to prevent loss of precision with floats. You should use Decimals everywhere, and SDK methods now return Decimals. See related breaking changes below:
Rate function now returns Decimals always, so there is no accurate parameter anymore (this behaviour was achieved before by rate(accurate=True)
)
Before, if some balance didn't exist in a wallet, it returned 0
(int), but if it existed it returned amount as string.
This inconsistent behaviour is now fixed, but now every amount is Decimal.
amount_BTC
, amount_LTC
, amount (BCH)
, etc. fields are now Decimals.
For convenience, amount_field
attribute was added on coin objects:
btc.amount_field # "amount_BTC"
getrequest
was renamed to get_request
addrequest
to add_request
addinvoice
to add_invoice
To follow PEP8.
Before fee callback function passed tx size and default fee (in satoshis) and expected to return btc amount.
Now it is expecting to return amount in satoshis for ease of use.
By adding a better exception system, and a major version change, we remove ValueError. bitcart.errors.BaseError
is now a base error for all exceptions. bitcart.errors.RequestError
is a base error for all errors returned from server. bitcart.errors.UnknownError
, if spec is unavailable works the same as ValueError
before
As internally all SDK code is now async, it is based on aiohttp server for webhooks. You should use aiohttp methods to set up custom servers if needed, flask is no longer a dependency.
As aiohttp is now used for webhooks, there is no need to install extra dependencies. SDK 1.0 doesn't have webhook extra.
As async and sync versions are now part of one library, it will be in bitcart
Pypi package. bitcart-async
package's last version will be 0.9.1.
All future updates will be made in bitcart
package.
They can be found in this PR comment
Fixed async timeouts Fixed timeout from 10 seconds to 5 minutes
To use proxy, install optional dependencies:
pip install bitcart[proxy]
for sync version, pip install bitcart-async[proxy]
for async version.
HTTP, SOCKS4 and SOCKS5 proxies supported.
To use, pass proxy url to coin constructor:
btc = BTC(proxy="socks5://localhost:9050")
Version 0.8.5: completely remove aiohttp warnings
This version removes a nasty unclosed session and connector warning, finally!
Bitcoin Cash support & misc fixes
This version adds bitcoin cash support plus probably forever fixes unclosed session warning (:
Added BSTY coin
Added validate_key method
You can now use validate_key method to ensure that key you are going to use restore wallet is valid, without actually restoring it.
Examples:
>>> c.validate_key("test")
False
>>> c.validate_key("your awesome electrum seed")
True
>>> c.validate_key("x/y/z pub/prv here")
True
Webhooks!
This update adds a new way of receiving updates, start a webhook and daemon will deliver updates to that webhook by itself.
To use that feature, pip install bitcart[webhook]
for sync version(flask), async version has that built-in(aiohttp).
To use that instead of polling, just replace
btc.poll_updates()
with
btc.start_webhook()
Remove async warning
This is a follow-up of previous release
Automatic session closing in async!
That's it, no need to use async with or manual .close() anymore in async version!
Added missing event and history() fixes
This bugfix fixes xpub sending, and exception raising.
Structural improvements and more
This version makes async version the default one used in the repo.
Pypi versions aren't changed, bitcart is sync version, bitcart-async is async.
But now there is a major difference, instead of maintaining both sync and async versions,
async version is in master, and sync version is generated using sync_generator.py
, which basically removes async's and awaits.
It means less time spent, and instead of porting new features to sync/async versions, now I will have more time spend on new features!
The async branch will be deleted.
Also, the long awaited pay_to_many
function to do batch transactions is there!
Both pay_to
and pay_to_many
now have optional feerate
, which is sat/vbyte rate.
Minimum possible is 1 sat/vbyte.
With bitcart you can get minimal fees, with no third parties!
Added support for both args and kwargs, fixes This version allows using SDK with both positional and by-name arguments.
Added missing pay_to_many
This release adds ability to create batch transactions, some examples:
>>> btc.pay_to_many([{"address":"mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt","amount":0.001}, {"address":"mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB","amount":0.0001}])
'60fa120d9f868a7bd03d6bbd1e225923cab0ba7a3a6b961861053c90365ed40a'
>>> btc.pay_to_many([("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt",0.001),("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB",0.0001)])
'd80f14e20af2ceaa43a8b7e15402d420246d39e235d87874f929977fb0b1cab8'
>>> btc.pay_to_many([("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt",0.001), ("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB",0.0001)], broadcast=False)
{'hex': '0200000...', 'complete': True, 'final': False}
Fix for latest daemon
This version adds new coin: gzro to bitcart. All APIs are the same, just import GZRO from bitcart.
Fee calculation func now recieves default fee as argument too
This version adds new coin: litecoin to bitcart.
This is where bitcart shows its features.
All APIs are the same, just import LTC from bitcart.
Full fee control and easy lightning.
Fee control:
Now you can pass fee parameter to pay_to function to specify manual fee, or callback function like:
def fee_calc(size):
return size/4
btc.pay_to(address, amount, fee=fee_calc)
Getting size as argument and returning fee, it can be any function of your choice.
Another parameter, broadcast, was added. By default transaction is submitted to network, but if you set broadcast to False raw transaction will be returned. See API reference in docs for details.
Easy lightning:
Now lightning is part of btc daemon and BTC coin class, just launch the same daemon and use all lightning functions!
After upgrade, try for example, this:
>>> btc.node_id
'030ff29580149a366bdddf148713fa808f0f4b934dccd5f7820f3d613e03c86e55'
Lightning is enabled in your daemon by default, disable it with BTC_LIGTNING=false environment variable.
If lightning is disabled, bitcart.errors.LightningDisabledError
is raised.
This version adds new events-based API to get updates.
To register a callback function, use add_event_handler(events, func)
function or @on
decorator
Example:
@btc.on("new_transaction")
def handler(event, tx):
print(event)
print(tx)
print(btc.get_tx(tx))
btc.poll_updates()
The following code would print
new_transaction
some_tx_hash
dict of tx hash data
On each transaction in your wallet.
btc.on
or add_event_handler
can also accept a list of events, for example:
def handler(event, **kwargs):
print(event, kwargs)
Getting updates is the same, via btc.poll_updates()
.
There are two kinds of events for now:
new_block
which gets emitted on every new block, passing height argument to callback function
new_transaction
which gets emitted on every new transaction passing tx argument as tx_hash of new transaction to callback_function.
Old @btc.notify
api is removed.
Fix warning raising(no stacklevel)
Added ability to get fiat price in most currencies
Now rate()
method accepts optional currency argument to get it in currency other than USD.
New method: list_fiat()
to get list of all supported fiat currencies.
Add btc.rate()
method to get USD price of bitcoin
Use requests.Session for better performance
This is a small bugfix to fix pip description rendering.
Version 0.2.0: Lightning update!
Lightning network support is now in bitcart as defined by bitcartcc/bitcart#51
This adds in new LN class and related methods.
Also, now it is not needed to fill in all values, some defaults are used:
rpc_user="electrum"
rpc_pass="electrumz"
rpc_url="http://localhost:5000" for bitcoin daemon and "http://localhost:5001" for lightning daemon.
When xpub is not provided, a warning is created.
- Add type hints everywhere
- Code is checked with mypy and pylint
- Docs now available(check readme)
- Automatic deployment via circleci
And many more...
Initial release