Web3.py extension for Tempo blockchain - adds support for AA transactions and Tempo-specific features
Project description
pytempo
[!IMPORTANT] This is a proof-of-concept, please reach out to the Tempo team if you are interested in using this library in production.
Web3.py extension for Tempo.
Installation
pip install -e .
Or with uv:
uv add .
Quick Start
Recommended: Typed API
from pytempo import TempoTransaction, Call
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,
fee_token="0x20c0000000000000000000000000000000000001",
calls=(Call.create(to="0xRecipient...", value=1000),),
)
signed_tx = tx.sign("0xYourPrivateKey...")
# Send using web3.py
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
Legacy API (Backwards Compatible)
from pytempo import patch_web3_for_tempo, create_tempo_transaction
from web3 import Web3
# Step 1: Patch web3.py to add Tempo support
# (Only needed if using web3's internal transaction parsing)
patch_web3_for_tempo()
# Step 2: Use web3.py normally with Tempo features
w3 = Web3(Web3.HTTPProvider("https://rpc.testnet.tempo.xyz"))
account = w3.eth.account.from_key("0x...")
# Step 3: Create Tempo AA transaction (Type 0x76)
tx = create_tempo_transaction(
to="0xRecipient...",
value=0,
gas=100000,
max_fee_per_gas=w3.eth.gas_price * 2,
max_priority_fee_per_gas=w3.eth.gas_price,
nonce=w3.eth.get_transaction_count(account.address),
chain_id=w3.eth.chain_id,
fee_token="0x20c0000000000000000000000000000000000001", # AlphaUSD
)
# Step 4: Sign and send using standard web3.py
tx.sign(account.key.hex())
tx_hash = w3.eth.send_raw_transaction(tx.encode())
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
Typed API (v0.3.0+)
The typed API provides immutable dataclasses with validation:
from pytempo import TempoTransaction, Call, AccessListItem
# All parameters passed directly to create()
tx = TempoTransaction.create(
chain_id=42429,
gas_limit=100_000,
max_fee_per_gas=2_000_000_000,
fee_token="0x20c0000000000000000000000000000000000001",
awaiting_fee_payer=True, # Mark for fee payer
calls=(
Call.create(to="0xRecipient...", value=1000, data="0xabcd"),
Call.create(to="0xOther...", value=2000), # Batch multiple calls
),
)
# Immutable signing - returns new transaction
signed = tx.sign("0xPrivateKey...")
assert tx.sender_signature is None # Original unchanged
assert signed.sender_signature is not None
Parsing from Dicts
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
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'
Legacy Usage
Basic Transaction
from pytempo import create_tempo_transaction
tx = create_tempo_transaction(
to="0xRecipient...",
value=1000000000000000,
gas=100000,
max_fee_per_gas=2000000000,
max_priority_fee_per_gas=2000000000,
nonce=0,
chain_id=42429,
)
tx.sign("0xYourPrivateKey...")
encoded = tx.encode()
With Custom Fee Token
tx = create_tempo_transaction(
to="0xRecipient...",
value=0,
fee_token="0xTokenAddress...", # Pay gas in this ERC-20 token
gas=100000,
max_fee_per_gas=2000000000,
max_priority_fee_per_gas=2000000000,
nonce=0,
chain_id=42429,
)
Gas Sponsorship
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,
fee_token="0xTokenAddress...",
awaiting_fee_payer=True, # Mark for fee payer
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)
# Send
w3.eth.send_raw_transaction(final_tx.encode())
Batch Multiple Calls
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
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
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...")
API Reference
TempoTransaction (Recommended)
Immutable, strongly-typed transaction (frozen dataclass).
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
Methods:
sign(private_key, for_fee_payer=False)- Sign transaction (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
patch_web3_for_tempo()
Monkey patches web3.py to recognize Tempo AA transactions. Must be called before using web3.
create_tempo_transaction(...) (Legacy)
Creates a mutable Tempo AA transaction.
Parameters:
to(str): Destination addressvalue(int): Value in wei (default: 0)gas(int): Gas limitmax_fee_per_gas(int): Maximum fee per gasmax_priority_fee_per_gas(int): Maximum priority fee per gasnonce(int): Transaction noncechain_id(int): Chain IDnonce_key(int): Nonce key for parallel execution (default: 0)fee_token(str, optional): ERC-20 token address for gas paymentcalls(list, optional): List of calls for batchingdata(str, optional): Transaction datavalid_before(int, optional): Timestamp before which tx is validvalid_after(int, optional): Timestamp after which tx becomes valid
Returns: LegacyTempoTransaction
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytempo-0.2.1.tar.gz.
File metadata
- Download URL: pytempo-0.2.1.tar.gz
- Upload date:
- Size: 197.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fdb87f6f4943484055d89ad33a3fe9ad6d3ba1d1eed12a1554ade58881f7d10
|
|
| MD5 |
5882aa1686c4e857fdacd1ccf672cb34
|
|
| BLAKE2b-256 |
47561f0eaaea8d6bb1cb168055c4bdbbfa07cf4a373ab6c77eb22bce1324bd93
|
File details
Details for the file pytempo-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pytempo-0.2.1-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1df605cf834ca46f1d4b2030d182c4153327b76afd45bd35a608d2f177052add
|
|
| MD5 |
a137bdadba861b31453a78ec5b9cbfb6
|
|
| BLAKE2b-256 |
9e315b4d76b1a48df5ded64a7b1622624bd4a47dd681b25ce9bb80a6bf012d8a
|