Library for trading on Tristero
Project description
Tristero
This repository is home to Tristero's trading library.
How it works
Tristero supports two primary swap mechanisms:
Permit2 Swaps (EVM-to-EVM)
- Quote & Approve - Request a quote and approve tokens via Permit2 (gasless approval)
- Sign & Submit - Sign an EIP-712 order and submit for execution
- Monitor - Track swap progress via WebSocket updates
Feather Swaps (UTXO-based)
- Quote & Deposit - Request a quote to receive a deposit address
- Manual Transfer - Send funds to the provided deposit address
- Monitor - Track swap completion via WebSocket updates
This library provides both high-level convenience functions and lower-level components for precise control.
Installation
pip install tristero
Environment Configuration
Tristero supports three environments: PRODUCTION (default), STAGING, and LOCAL.
Set the environment globally at startup:
from tristero import set_config
set_config("STAGING") # all subsequent calls use the staging API
Or override per call:
quote = await get_swap_quote(..., env="LOCAL")
Every user-facing function in the SDK accepts an optional env keyword argument.
Quick Start
Spot Swap (quote, sign, submit)
import asyncio
import json
import os
from eth_account import Account
from tristero import get_swap_quote, sign_and_submit, make_async_w3
async def main() -> None:
private_key = os.getenv("TEST_ACCOUNT_PRIVKEY")
if not private_key:
raise RuntimeError("Set TEST_ACCOUNT_PRIVKEY")
wallet = Account.from_key(private_key).address
w3 = make_async_w3(os.getenv("ARB_RPC_URL", "https://arbitrum-one-rpc.publicnode.com"))
# 1. Get a quote (USDC -> WETH on Arbitrum)
quote = await get_swap_quote(
wallet=wallet,
src_chain=42161,
src_token="0xaf88d065e77c8cC2239327C5EDb3A432268e5831", # USDC
dst_chain=42161,
dst_token="0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", # WETH
amount=1_000_000, # 1 USDC (6 decimals)
)
print(json.dumps(quote, indent=2))
# 2. Sign and submit (w3 required for Permit2 approval on source chain)
result = await sign_and_submit(quote, private_key, w3=w3, wait=True, timeout=300)
print(result)
asyncio.run(main())
Margin Position (quote, sign, submit)
import asyncio
import json
import os
from eth_account import Account
from tristero import get_swap_quote, sign_and_submit
async def main() -> None:
private_key = os.getenv("TEST_ACCOUNT_PRIVKEY")
if not private_key:
raise RuntimeError("Set TEST_ACCOUNT_PRIVKEY")
wallet = Account.from_key(private_key).address
# 1. Get a margin quote (2x leveraged USDC/WETH on Arbitrum)
quote = await get_swap_quote(
wallet=wallet,
src_chain=42161,
src_token="0xaf88d065e77c8cC2239327C5EDb3A432268e5831", # USDC (collateral)
dst_chain=42161,
dst_token="0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", # WETH (base)
amount=1_000_000, # 1 USDC collateral (6 decimals)
leverage=2,
)
print(json.dumps(quote, indent=2))
# 2. Sign and submit (no w3 needed for margin)
result = await sign_and_submit(quote, private_key, wait=True, timeout=120)
print(result)
asyncio.run(main())
More Examples
Spot Swap (direct execution)
import os
import asyncio
from eth_account import Account
from tristero import ChainID, TokenSpec, execute_permit2_swap, make_async_w3
async def main() -> None:
private_key = os.getenv("TEST_ACCOUNT_PRIVKEY")
if not private_key:
raise RuntimeError("Set TEST_ACCOUNT_PRIVKEY")
account = Account.from_key(private_key)
arbitrum_rpc = os.getenv("ARB_RPC_URL", "https://arbitrum-one-rpc.publicnode.com")
w3 = make_async_w3(arbitrum_rpc)
result = await execute_permit2_swap(
w3=w3,
account=account,
src_t=TokenSpec(chain_id=ChainID(42161), token_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), # USDC (Arbitrum)
dst_t=TokenSpec(chain_id=ChainID(8453), token_address="0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"), # USDT (Base)
raw_amount=1_000_000, # 1 USDC (6 decimals)
timeout=300,
)
print(result)
asyncio.run(main())
Margin: Direct Open
import asyncio
import os
from eth_account import Account
from tristero import open_margin_position
async def main() -> None:
private_key = os.getenv("TEST_ACCOUNT_PRIVKEY", "")
if not private_key:
raise RuntimeError("Set TEST_ACCOUNT_PRIVKEY")
wallet = Account.from_key(private_key).address
result = await open_margin_position(
private_key=private_key,
chain_id="42161",
wallet_address=wallet,
quote_currency="0xaf88d065e77c8cC2239327C5EDb3A432268e5831", # USDC
base_currency="0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", # WETH
leverage_ratio=2,
collateral_amount="1000000", # 1 USDC (6 decimals)
wait_for_result=True,
timeout=120,
)
print(result)
asyncio.run(main())
Margin: List Positions / Close Position
import asyncio
import os
from eth_account import Account
from tristero import close_margin_position, list_margin_positions
async def main() -> None:
private_key = os.getenv("TEST_ACCOUNT_PRIVKEY", "")
if not private_key:
raise RuntimeError("Set TEST_ACCOUNT_PRIVKEY")
wallet = Account.from_key(private_key).address
positions = await list_margin_positions(wallet)
open_pos = next((p for p in positions if p.status == "open"), None)
if not open_pos:
raise RuntimeError("no open positions")
result = await close_margin_position(
private_key=private_key,
chain_id="42161",
position_id=open_pos.taker_token_id,
escrow_contract=open_pos.escrow_address,
authorized=open_pos.filler_address,
cash_settle=False,
fraction_bps=10_000,
deadline_seconds=3600,
wait_for_result=True,
timeout=120,
)
print(result)
asyncio.run(main())
Feather: Start (get deposit address)
Feather swaps are deposit-based: you start an order to receive a deposit_address, send funds to it manually, then optionally wait for completion.
Submit only:
import asyncio
from tristero import ChainID, TokenSpec, start_feather_swap
async def main() -> None:
# Example: ETH (native) -> XMR (native)
src_t = TokenSpec(chain_id=ChainID.ethereum, token_address="native")
dst_t = TokenSpec(chain_id=ChainID.monero, token_address="native")
# Replace with your own destination address on the destination chain.
dst_addr = "YOUR_XMR_ADDRESS"
swap = await start_feather_swap(
src_t=src_t,
dst_t=dst_t,
dst_addr=dst_addr,
raw_amount=100_000_000_000_000_000, # 0.1 ETH in wei
)
order_id = (
(swap.data or {}).get("id")
or (swap.data or {}).get("order_id")
or (swap.data or {}).get("orderId")
or ""
)
print("order_id:", order_id)
print("deposit_address:", swap.deposit_address)
asyncio.run(main())
Submit + wait (WebSocket):
import asyncio
from tristero import ChainID, OrderType, TokenSpec, start_feather_swap, wait_for_completion
async def main() -> None:
src_t = TokenSpec(chain_id=ChainID.ethereum, token_address="native")
dst_t = TokenSpec(chain_id=ChainID.monero, token_address="native")
dst_addr = "YOUR_XMR_ADDRESS"
swap = await start_feather_swap(
src_t=src_t,
dst_t=dst_t,
dst_addr=dst_addr,
raw_amount=100_000_000_000_000_000,
)
order_id = (
(swap.data or {}).get("id")
or (swap.data or {}).get("order_id")
or (swap.data or {}).get("orderId")
or ""
)
if not order_id:
raise RuntimeError(f"Feather swap response missing order id: {swap.data}")
print("deposit_address:", swap.deposit_address)
print("Waiting for completion...")
completion = await wait_for_completion(order_id, order_type=OrderType.FEATHER)
print(completion)
asyncio.run(main())
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 tristero-0.3.5.tar.gz.
File metadata
- Download URL: tristero-0.3.5.tar.gz
- Upload date:
- Size: 50.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d60669fa89528fb727caefd0bc9b99adb151f402c35738dc87d718a4ec2a5dfe
|
|
| MD5 |
be4b6aef75059974bf4c9f2ae598fd0e
|
|
| BLAKE2b-256 |
1143c4b26a8a37552d895a359107e05bf96643ab00655eae4f5ab2e52dc6d035
|
File details
Details for the file tristero-0.3.5-py3-none-any.whl.
File metadata
- Download URL: tristero-0.3.5-py3-none-any.whl
- Upload date:
- Size: 53.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8560d7c0ea785a5ea458b392e0af508c965d090b05123c171413e6856eea2cb7
|
|
| MD5 |
770c1db07bc398a9fac76036af165c26
|
|
| BLAKE2b-256 |
495384128f42851efc13dcf68de238539dbb91d060cd09b87a5a46464c6461be
|