Python library for the Warthog cryptocurrency.
This is the Python port of warthog-ts and provides type-safe primitives for building and submitting transactions on the Warthog network, including the full native DeFi transaction set:
wartTransfertokenTransferassetCreationlimitSwapliquidityDepositliquidityWithdrawalcancelation
The package is not yet published to PyPI. Install it directly from the GitHub repository using uv:
uv add "warthog_py @ git+https://github.com/warthog-network/warthog_py.git"Or add it to your pyproject.toml:
[project]
dependencies = [
"warthog_py @ git+https://github.com/warthog-network/warthog_py.git",
]- Python
>=3.10 - Dependencies (
coincurve,mnemonic,bip32,pycryptodome,requests) are installed automatically byuv sync.
from warthog_py import (
Account,
Address,
NonceId,
RoundedFee,
WarthogLegacyApi,
Wart,
)
# 1. Load or generate your account
account = Account.from_private_key_hex("your-private-key-hex")
# 2. Prepare the recipient
recipient = Address.from_hex("0000000000000000000000000000000000000000de47c9b2")
# 3. Connect to a node. The legacy node (core/master, core/dev) takes one
# kind of transaction, a transfer. The first known legacy node is a public
# main net node (port 3001).
api = WarthogLegacyApi()
# For a local core/master node: WarthogLegacyApi("http://127.0.0.1:3100")
# For the DeFi node: WarthogDeFiApi("http://127.0.0.1:3101")
# 4. Fetch chain head and build a transaction context
ctx = api.create_transaction_context(RoundedFee.min(), NonceId.random())
# 5. Build and sign a WART transfer
tx = ctx.transfer_wart(account, recipient, Wart.from_e8(100_000_000))
# 6. Submit
result = api.submit_transaction(tx)
print(result["txHash"])| Module | Purpose |
|---|---|
warthog_py.Address |
20-byte SHA-256-checksummed addresses |
warthog_py.NonceId |
32-bit transaction nonces |
warthog_py.TokenDecimals |
Number of decimal places for a token |
warthog_py.ParsedFunds |
Parsed decimal strings |
warthog_py.Funds |
Token amounts with decimals |
warthog_py.Wart |
Native WART token (8 decimals) |
warthog_py.Liquidity |
Liquidity pool tokens (8 decimals) |
warthog_py.RoundedFee |
Rounded transaction fees |
warthog_py.CompactFee |
16-bit compact fee representation |
warthog_py.Price |
Normalized mantissa/exponent prices |
warthog_py.Account |
secp256k1 wallet account |
warthog_py.HDWallet |
BIP-44 hierarchical deterministic wallet |
warthog_py.ChainPin |
Proposed (height, hash) pin for transaction building |
warthog_py.ChainHead |
Normalized /chain/head view |
warthog_py.NodeVersion |
The version a node reports about itself |
warthog_py.DeFiWartBalance |
WART balance on the DeFi node |
warthog_py.TokenBalance |
Token balance on the DeFi node |
warthog_py.AnyTransactionContext |
Union of the two transaction contexts |
warthog_py.WarthogLegacyTransactionContext |
Transaction builder for the legacy node |
warthog_py.WarthogDeFiTransactionContext |
Transaction builder for the DeFi node |
warthog_py.WarthogApiBase |
HTTP plumbing shared by the node clients |
warthog_py.WarthogLegacyApi |
Client for the legacy node |
warthog_py.WarthogDeFiApi |
Client for the DeFi node |
The two node families are separate APIs, not one API with a mode, so there is a client and a transaction context for each.
| Legacy node | DeFi node | |
|---|---|---|
| Node | core/master, core/dev |
core/defi |
| Chain head | flat | nested under chainHead |
| Transactions | a transfer only | nine kinds |
| WART amount field | amountE8 |
wartE8 |
Payload type |
none | required |
| Client | WarthogLegacyApi |
WarthogDeFiApi |
| Context | WarthogLegacyTransactionContext |
WarthogDeFiTransactionContext |
Neither node reads the other's amount field, so exactly one of them may be sent. The signed bytes are the same in both, so a signature made for one family is valid for the other.
A legacy context cannot build a limit swap, because that builder does not exist on it. Code that only sends WART can hold either context:
from warthog_py import AnyTransactionContext
def send_wart(ctx: AnyTransactionContext, account, recipient, wart):
tx = ctx.transfer_wart(account, recipient, wart)
...from warthog_py import WarthogApiBase, WarthogDeFiApi, WarthogLegacyApi
legacy = WarthogLegacyApi()
defi = WarthogDeFiApi()
WarthogApiBase.known_nodes_legacy()
WarthogApiBase.known_nodes_defi()
# The legacy node reports one number: the confirmed balance.
wart = legacy.get_wart_balance(address)
# The DeFi node reports the total, what is locked in open limit orders and
# what is locked by transactions in the mempool.
balance = defi.get_wart_balance(address)
free = balance.free()
# A token balance, the DeFi node only.
token = defi.get_token_balance(address, asset_hash)Ask a node which version it is running. Both families report the same fields, on different paths.
version = legacy.get_node_version() # /tools/version
version = defi.get_node_version() # /version
version.name # 'v0.10.3 "dd054a2"', the version string with the commit
version.major # 0
version.minor # 10
version.patch # 3
version.commit # "dd054a2"
# The dotted triple, for comparisons
version.version # "0.10.3"Following standard Python practice, every factory ships in two flavors:
name(...)— raises a custom*Erroron invalid input (InvalidAddressError,InvalidNonceError, etc.).try_name(...)— returnsNoneon invalid input instead of raising.
Address.from_hex("0000000000000000000000000000000000000000de47c9b2") # raises on invalid
Address.try_from_hex("0000000000000000000000000000000000000000de47c9b2") # None on invalid
Address.validate("0000000000000000000000000000000000000000de47c9b2") # boolThe custom exception hierarchy lives in warthog_py.errors. All exceptions inherit from WarthogPyError.
This project uses uv for dependency management. From the project root:
# Once: sync dependencies (creates .venv automatically and installs everything)
uv sync
# Every time: run the tests
uv run pytest
python examples/transactions.pyTo bump the version in pyproject.toml (using uv's built-in version command):
uv version --bump patch # 0.1.0 -> 0.1.1
uv version --bump minor # 0.1.0 -> 0.2.0
uv version --bump major # 0.1.0 -> 1.0.0Use --no-sync to skip the venv rebuild:
uv version --bump patch --no-syncReleased under the MIT License.