Skip to main content

PropAMM Python SDK

An async Python SDK for the PropAMMRouter contract, built on web3.py. Features:

  • Typed router bindings: quote and swap across all whitelisted venues, a single venue, or a chosen subset, plus the router's views and ERC-20 approvals.
  • Accurate quotes: quotes automatically apply fresh pAMM state overrides, so they price live off-chain liquidity rather than stale on-chain state.
  • Helpers: amount/unit conversion, slippage, deadlines, and well-known token and venue addresses.
  • A typed error hierarchy: contract reverts surface as human-readable errors (e.g. InsufficientOutput(...)).

Method names drop the on-chain V1 suffix (router.swap(...) calls swapV1). A client is read-only by default; pass an account to send transactions.

Setup

Using uv (installs the exact pinned versions from uv.lock):

uv sync --extra dev
uv run pytest          # incl. ABI selector & overrides regression tests
uv run ruff check .

Using pip (resolves the dependency ranges in pyproject.toml):

python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
ruff check .

Getting started

Quote and swap 1 ETH for USDC through the best venue:

import asyncio
from propamm import ContractClient, PropAmmRouter, SwapParams
from propamm.common.accounts import account_from_key
from propamm.common.helpers import apply_slippage, deadline_in, parse_ether
from propamm.common.tokens import ETH_SENTINEL, USDC

PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" # anvil rich account
RPC_URL = "http://localhost:8545" # local anvil instance
ROUTER_ADDRESS = "0x4DdF368080CD7946db5b459aD591c350158175e1" # mainnet router deployment

async def main():
    account = account_from_key(PRIVATE_KEY)
    client = ContractClient(RPC_URL, account=account)
    router = PropAmmRouter(client, ROUTER_ADDRESS)

    amount_in = parse_ether("1")
    quote = await router.quote(ETH_SENTINEL, USDC, amount_in)

    result = await router.swap_and_wait(
        SwapParams(
            token_in=ETH_SENTINEL,
            token_out=USDC,
            amount_in=amount_in,
            amount_out_min=apply_slippage(quote.amount_out, 50),  # quote - 0.5%
            recipient=account.address,
            deadline=deadline_in(300),  # now + 5 min
        )
    )
    print(f"received {result.amount_out} USDC via {result.executed_venue}")


asyncio.run(main())

A runnable version lives in examples/getting_started.py.

python3 examples/getting_started.py

It defaults to a local anvil mainnet fork (anvil --fork-url <mainnet rpc>) with anvil's default funded account and the mainnet router deployment. Override with RPC_URL / PRIVATE_KEY / ROUTER_ADDRESS / SLIPPAGE_BPS.

Layout

Module Purpose
propamm.client ContractClient: AsyncWeb3 wrapper (contract / call_with_overrides / send / wait_for_transaction).
propamm.router PropAmmRouter bindings: quotes, swaps, ERC-20, views.
propamm.router.abi Vendored router ABI and custom-error naming.
propamm.overrides pAMM state-override sources (OverridesRpcSource, OverridesWsSource).
propamm.prices PriceLevels client + price-level sources (PriceLevelsRpcSource, PriceLevelsWsSource) and the Titan quote helpers.
propamm.common tokens, pamms, helpers, accounts.

Quotes & state overrides

Quote functions are nonpayable on-chain (not view), so they run through eth_call simulation. By default each quote attaches the latest pAMM state overrides (streamed from Titan via OverridesWsSource) plus the snapshot's block number/timestamp, so venues price fresh off-chain liquidity. Pass QuoteOptions(overrides=None) to quote without overrides, or supply your own OverridesSource / OverridesSnapshot.

Note: quotes apply fresh overrides, but a fork still executes swaps against its frozen state — if a swap reverts with InsufficientOutput on a fork, raise the slippage. Live chains fill at the quoted state normally.

Price levels

Alongside the raw state overrides, Titan publishes prices it has already quoted, grouped per pAMM: for each pair, an order_book of rungs mapping an input amount to the output it would receive. This lets a taker read prices across a range of trade sizes without an eth_call per size. Rungs are either Simulated (from an EVM simulation) or Interpolated (a linear spline between simulated rungs, for finer granularity).

The PriceLevels client wraps it, mirroring PropAmmRouter: a single class with a default snapshot source you can override.

from propamm import PriceLevels
from propamm.common.helpers import parse_units
from propamm.common.tokens import USDC, WETH

prices = PriceLevels()  # default: one-shot HTTP snapshot source

snapshot = await prices.get_price_levels()
# snapshot.pamms[i].pairs[j].order_book -> [PriceLevel(amount_in, amount_out, variant), ...]

# Quote helpers are served from Titan's latest snapshot over HTTP, skipping the
# on-chain eth_call that router.quote runs.
best = await prices.get_quote(USDC, WETH, parse_units("1000", 6))
# TitanQuote(token_in, token_out, amount_in, amount_out, pamm, router, block_number, slot, ...)
pinned = await prices.get_quote_venue(best.pamm, USDC, WETH, parse_units("1000", 6))

The snapshot source defaults to a PriceLevelsRpcSource (one titan_getPammPriceLevels call per get_price_levels). For a live feed, pass a PriceLevelsWsSource instead — it streams complete snapshots, reconnects with backoff, and idle auto-closes, like OverridesWsSource. The stream is served from regional hosts (eu., ap., us.); pick the nearest:

from propamm import PriceLevels, PriceLevelsWsSource

prices = PriceLevels(
    PriceLevelsWsSource("wss://eu.rpc.titanbuilder.xyz/ws/pamm_price_levels")
)
snapshot = await prices.get_price_levels()  # served from the live stream
await prices.close()  # close the stream socket when done (no-op for the HTTP default)

Passing a PriceLevelsRpcSource with a custom URL instead points both the snapshots and the quote helpers at that endpoint (the quotes are HTTP-only).

A runnable version lives in examples/price_levels.py (python3 examples/price_levels.py; override the HTTP endpoint with PRICE_LEVELS_URL).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

propamm-1.1.2.tar.gz (176.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

propamm-1.1.2-py3-none-any.whl (32.7 kB view details)

Uploaded Python 3

File details

Details for the file propamm-1.1.2.tar.gz.

File metadata

  • Download URL: propamm-1.1.2.tar.gz
  • Upload date:
  • Size: 176.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for propamm-1.1.2.tar.gz
Algorithm Hash digest
SHA256 822a6d043d363a69702939ed1e592194c848ddb082a071d1205fc68bac3c8351
MD5 348cd1bab63ed07b2f01c742425523ad
BLAKE2b-256 636e1f2ae2b543c341a3dad0f1656fb9e6e84dcf1f553e2d342c7e157b4b03db

See more details on using hashes here.

File details

Details for the file propamm-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: propamm-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for propamm-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d37ce79bf5a61fa0003d45f85b8f554d21fc1ef87a7918ebc20decdcabbf7d88
MD5 1499b1f434b2704c32838f37bdc99cce
BLAKE2b-256 f930cc9282e6aedb4d1b05b60fedbc1f2a4ad2fc721fecd05336e4a267a04a35

See more details on using hashes here.

Release history Release notifications | RSS feed

1.1.3

2 files

This release

1.1.2 This release

2 files

1.1.1

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page