Skip to main content


Tempo wordmark



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 coercion
  • TempoTransaction.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 wei
  • max_priority_fee_per_gas (int) - Max priority fee per gas in wei
  • nonce (int) - Transaction nonce
  • nonce_key (int) - Nonce key for parallel execution
  • valid_before (int, optional) - Expiration timestamp
  • valid_after (int, optional) - Activation timestamp
  • fee_token (str/bytes, optional) - Fee token address
  • awaiting_fee_payer (bool) - Mark for fee payer signature
  • calls (tuple[Call, ...]) - Tuple of Call objects
  • access_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_gas from a live fee estimate rather than a hardcoded constant; the 2_000_000_000 values 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 transmission
  • hash() - Get transaction hash
  • get_signing_hash(for_fee_payer=False) - Get hash to sign
  • vrs() - Get v, r, s values
  • validate() - 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); inherits FeeAMM
  • Nonce — 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 transfer
  • basic_transaction.py - Transaction with fee token
  • fee_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)

Source distribution for pytempo 0.6.1
File Size Uploaded
pytempo-0.6.1.tar.gz 246.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytempo 0.6.1
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page