pytempo
Web3.py extension for Tempo.
Installation
pip install pytempo
Or with uv:
uv add pytempo
Quick Start
Typed Contract Helpers (v0.4.0+)
Use the built-in typed helpers for Tempo precompiles — no ABI knowledge needed:
from pytempo import TempoTransaction
from pytempo.contracts import TIP20, StablecoinDEX, ALPHA_USD, BETA_USD
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://rpc.testnet.tempo.xyz"))
# Create a strongly-typed immutable transaction
# No patching needed - we handle encoding ourselves
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
max_priority_fee_per_gas=1_000_000_000,
nonce=0,
calls=(
TIP20(ALPHA_USD).approve(spender=StablecoinDEX.ADDRESS, amount=10**18),
StablecoinDEX.place(token=BETA_USD, amount=100_000_000, is_bid=True, tick=10),
),
)
signed_tx = tx.sign("0xYourPrivateKey...")
# Send using web3.py
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
Manual Calls (v0.2.1+)
For arbitrary contract calls or simple transfers, use Call.create() directly:
from pytempo import TempoTransaction, Call
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://rpc.testnet.tempo.xyz"))
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
max_priority_fee_per_gas=1_000_000_000,
nonce=0,
calls=(Call.create(to="0xRecipient...", value=1000),),
)
signed_tx = tx.sign("0xYourPrivateKey...")
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
Custom Fee Tokens (v0.2.1+)
from pytempo.contracts import BETA_USD
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
fee_token=BETA_USD,
calls=(Call.create(to="0xRecipient...", value=1000),),
)
Gas Sponsorship (v0.2.1+)
from pytempo import TempoTransaction, Call
# User creates and signs a sponsored transaction
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
awaiting_fee_payer=True,
calls=(Call.create(to="0xRecipient...", value=1000),),
)
signed_tx = tx.sign("0xUserPrivateKey...")
# Fee payer signs (pays gas)
final_tx = signed_tx.sign("0xFeePayerPrivateKey...", for_fee_payer=True)
w3.eth.send_raw_transaction(final_tx.encode())
Batch Multiple Calls (v0.2.1+)
from pytempo import TempoTransaction, Call
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=200_000,
max_fee_per_gas=2_000_000_000,
calls=(
Call.create(to="0xAddress1...", value=100000),
Call.create(to="0xAddress2...", value=200000, data="0xabcdef"),
),
)
signed_tx = tx.sign("0xPrivateKey...")
Parallel Nonces (v0.2.1+)
from pytempo import TempoTransaction, Call
# Use different nonce keys for parallel execution
tx1 = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
nonce=0,
nonce_key=1, # First parallel key
calls=(Call.create(to="0xRecipient..."),),
)
tx2 = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
nonce=0,
nonce_key=2, # Second parallel key
calls=(Call.create(to="0xRecipient..."),),
)
# Both can be executed in parallel
Contract Creation (v0.2.1+)
from pytempo import TempoTransaction, Call
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=500_000,
calls=(Call.create(to=b"", data="0x6080604052..."),), # Empty 'to' for creation
)
signed_tx = tx.sign("0xPrivateKey...")
Parsing from Dicts (v0.2.1+)
from pytempo import TempoTransaction
# Supports both camelCase and snake_case keys
tx = TempoTransaction.from_dict({
"chainId": 42429,
"gas": 100_000,
"maxFeePerGas": 2_000_000_000,
"to": "0xRecipient...",
"value": 1000,
})
Type Coercion Helpers (v0.2.1+)
from pytempo import as_address, as_hash32, as_bytes, as_optional_address
# Validate and convert addresses
addr = as_address("0xF0109fC8DF283027b6285cc889F5aA624EaC1F55") # -> bytes (20)
addr = as_address(b"\x00" * 20) # Also accepts bytes
# Optional addresses (treats empty as None)
addr = as_optional_address("0x") # -> None
addr = as_optional_address(None) # -> None
# Validate 32-byte hashes
h = as_hash32("0x" + "ab" * 32) # -> bytes (32)
# Convert hex strings to bytes
data = as_bytes("0xabcdef") # -> b'\xab\xcd\xef'
API Reference
TempoTransaction
Immutable, strongly-typed transaction (frozen attrs model).
Factory Methods:
TempoTransaction.create(**kwargs)- Create with type coercionTempoTransaction.from_dict(d)- Parse from camelCase/snake_case dict
Create Parameters:
chain_id(int) - Chain ID (default: 1)gas_limit(int) - Gas limit (default: 21_000)max_fee_per_gas(int) - Max fee per gas in weimax_priority_fee_per_gas(int) - Max priority fee per gas in weinonce(int) - Transaction noncenonce_key(int) - Nonce key for parallel executionvalid_before(int, optional) - Expiration timestampvalid_after(int, optional) - Activation timestampfee_token(str/bytes, optional) - Fee token addressawaiting_fee_payer(bool) - Mark for fee payer signaturecalls(tuple[Call, ...]) - Tuple of Call objectsaccess_list(tuple[AccessListItem, ...]) - EIP-2930 access list
Note (T7 / TIP-1067): the base fee is now dynamic — it floats within a protocol-bounded range instead of being fixed, rising under load and decaying when idle. The range is capped below the old fixed base fee, so fees never exceed pre-T7 pricing. Still set
max_fee_per_gasfrom a live fee estimate rather than a hardcoded constant; the2_000_000_000values in the examples above are illustrative only.
Methods:
sign(private_key, for_fee_payer=False)- Sign transaction (returns new instance)sign_access_key(access_key_private_key, root_account)- Sign with access key (returns new instance)encode()- Encode to bytes for transmissionhash()- Get transaction hashget_signing_hash(for_fee_payer=False)- Get hash to signvrs()- Get v, r, s valuesvalidate()- Validate transaction fields
Call
Single call in a batch transaction.
Call.create(to, value=0, data=b"")- Create with type coercion
AccessListItem
EIP-2930 access list entry.
AccessListItem.create(address, storage_keys=())- Create with type coercion
Contract Helpers
Typed call builders for Tempo precompiles and tokens:
TIP20— TIP-20 token operations (transfer, approve, mint, burn, permit)StablecoinDEX— Stablecoin DEX operations (place, cancel, swap, withdraw, storage_credits query)AccountKeychain— Access key management (authorize, revoke, spending limits, queries)FeeAMM— Fee AMM liquidity operations (mint, burn, rebalance_swap)FeeManager— Fee manager operations (set fee token, distribute fees); inheritsFeeAMMNonce— Nonce precompile queries (get_nonce)CurrentCommittee— Current effective validator committee queries (get_committee_members)StorageCredits— T7 storage-credit accounting (set_mode, set_budget, balance/mode/budget queries)
Storage Credits (T7 / TIP-1060, v0.6.0+)
Storage credits offset the cost of reusing previously freed storage. Each account earns a credit when it deletes one of its own storage slots and can apply it to a later storage creation via a per-transaction mode.
from pytempo import TempoTransaction
from pytempo.contracts import StorageCredits, StorageCreditMode
# Read the persistent credit balance for an account
balance = StorageCredits.balance_of(w3, account="0xYourAddress...")
# Select a storage-creation mode for the current transaction.
# NOTE: mode/budget are transaction-local (reset every tx) and must come
# BEFORE the storage-creating calls they affect.
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=200_000,
max_fee_per_gas=2_000_000_000,
calls=(
StorageCredits.set_mode(StorageCreditMode.PRESERVE),
# ... subsequent calls that create storage ...
),
)
Modes: REFUND (default, simulation-safe), PRESERVE (never consume credits),
DIRECT (consume synchronously; set_budget(n) bounds spend to n, while
set_mode(DIRECT) uses uint64 max).
Development
make install # Install dependencies
make test # Run tests
make lint # Lint
make format # Format code
make check # Run all checks (lint + format-check + test)
Examples
See the examples/ directory:
simple_send.py- Simple value transferbasic_transaction.py- Transaction with fee tokenfee_payer_sponsored.py- Gas sponsorship and call batching
Contributing
Our contributor guidelines can be found in CONTRIBUTING.md.
Security
See SECURITY.md. Note: Tempo is still undergoing audit and does not have an active bug bounty. Submissions will not be eligible for a bounty until audits have concluded.
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Metadata
Release files for pytempo 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytempo-0.6.1.tar.gz | 246.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytempo-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 304.3 kB
Release files / pytempo-0.6.1.tar.gz
| Download URL | pytempo-0.6.1.tar.gz |
|---|---|
| Size | 246.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
926fa11f46c24a2a3e38ffe2f648799f2c0a44a872a1aaece2713d7280f3a2f4
|
|
BLAKE2b-256 checksum How to use checksums |
14dd001b61a6d1c6ae6c55d25be5fbdb757a22c641a8c666ac4d9097c9782b13
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / pytempo-0.6.1-py3-none-any.whl
| Download URL | pytempo-0.6.1-py3-none-any.whl |
|---|---|
| Size | 57.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eef20ba331c91086075052e64fd3ff9c1b5f98b44f83aa5dbcf16265fdbc55a7
|
|
BLAKE2b-256 checksum How to use checksums |
2f9c26c74180dcf5c8bf7e088a13b4f88776a1effaf408cfc766f202ece4afa2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|