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
Quick Start
Execute a cross-chain swap in just a few lines:
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("ARBITRUM_RPC_URL", "https://arbitrum-one-rpc.publicnode.com")
w3 = make_async_w3(arbitrum_rpc)
# Example: USDC on Arbitrum -> USDT on Base
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())
Usage Examples
Quote (read-only)
import asyncio
import os
from eth_account import Account
from tristero import ChainID, get_quote
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
quote = await get_quote(
from_wallet=wallet,
to_wallet=wallet,
from_chain_id=str(ChainID(42161).value),
from_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831", # USDC (Arbitrum)
to_chain_id=str(ChainID(42161).value),
to_address="0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", # WETH (Arbitrum)
amount=1_000_000,
)
print(quote)
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
chain_id = "42161"
quote_currency = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" # USDC (Arbitrum)
base_currency = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1" # WETH (Arbitrum)
leverage = 2
collateral = "1000000" # 1 USDC (6 decimals)
result = await open_margin_position(
private_key=private_key,
chain_id=chain_id,
wallet_address=wallet,
quote_currency=quote_currency,
base_currency=base_currency,
leverage_ratio=leverage,
collateral_amount=collateral,
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
chain_id = "42161"
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=chain_id,
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.2.tar.gz.
File metadata
- Download URL: tristero-0.3.2.tar.gz
- Upload date:
- Size: 47.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","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 |
82ceb4280dd10a6b4748032b60e3d742e2efe7e919d6f32d447ba04ccf18c0d8
|
|
| MD5 |
a420943fbdc441b18b529a77015c0bb6
|
|
| BLAKE2b-256 |
7bd088c965ea5aca02c2ebe50d0be59504b182460dcc2a15e71fe55f308bfaa8
|
File details
Details for the file tristero-0.3.2-py3-none-any.whl.
File metadata
- Download URL: tristero-0.3.2-py3-none-any.whl
- Upload date:
- Size: 50.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","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 |
ff8988fe8fd90af09f12730046fed61744a34ccb83e320992cf070d57650e23a
|
|
| MD5 |
b0f7838e2fd7e7dcd585963f6573909c
|
|
| BLAKE2b-256 |
4dd20d61c9213d507f382a9563a9f1727aff937d4f58591785946ffa3103d1d5
|