Metadata-Version: 2.3
Name: tristero
Version: 0.2.1
Summary: Library for trading on Tristero
Author: pty1
Author-email: pty1 <pty11@proton.me>
Requires-Dist: eth-account>=0.8.0
Requires-Dist: glom>=25.12.0
Requires-Dist: httpx>=0.23.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: tenacity>=8.0.0
Requires-Dist: web3>=6.0.0
Requires-Dist: websockets>=10.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# Tristero
[![PyPI version](https://badge.fury.io/py/tristero.svg)](https://badge.fury.io/py/tristero)
[![Python Support](https://img.shields.io/pypi/pyversions/tristero.svg)](https://pypi.org/project/tristero/)

This repository is home to Tristero's trading library.

### Installation
```
pip install tristero
```

### Quick Start

Execute a cross-chain swap in just a few lines:

```py
import os
from tristero.client import TokenSpec, execute_swap
from eth_account import Account
from web3 import AsyncWeb3
from tristero.api import ChainID

private_key = os.getenv("EVM_PRIVATE_KEY")
account = Account.from_key(private_key)
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))

result = await execute_swap(
    w3=w3,
    account=account,
    src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"),  # USDT
    dst_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"),  # USDC
    raw_amount=10000000  # Raw token amount (multiply by 10^decimals)
)
```

### Usage Examples

#### Permit2 Swap (EVM-to-EVM)

Permit2 swaps enable seamless EVM-to-EVM cross-chain token transfers with gasless approvals and EIP-712 signed orders.

```py
import os
from tristero.client import TokenSpec, execute_permit2_swap
from eth_account import Account
from web3 import AsyncWeb3
from tristero.api import ChainID

private_key = os.getenv("EVM_PRIVATE_KEY")
account = Account.from_key(private_key)
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://mainnet.base.org"))

# Example: USDC on Base → USDT on Avalanche
result = await execute_permit2_swap(
    w3=w3,
    account=account,
    src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"),  # USDC on Base
    dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"),  # USDT on Avalanche
    raw_amount=10 * 10**6  # 10 USDC (6 decimals)
)

print(f"Swap completed: {result}")
```

#### Feather Swap (UTXO-to-UTXO)

Feather swaps facilitate cross-chain transfers between UTXO-based chains like Bitcoin and Monero, providing a deposit address for manual fund transfers.

```py
import asyncio
from tristero.client import TokenSpec, start_feather_swap, wait_for_feather_completion
from tristero.api import ChainID

# Example: Bitcoin → Monero
async def btc_to_xmr_swap():
    # Start the swap to get a deposit address
    swap_result = await start_feather_swap(
        src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"),  # BTC
        dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"),   # XMR
        dst_addr="YOUR_XMR_RECEIVING_ADDRESS",  # Your Monero destination address
        raw_amount=100000  # Amount in satoshis (0.001 BTC)
    )
    
    print(f"Send {swap_result.data['amount']} satoshis to: {swap_result.deposit_address}")
    
    # Wait for swap completion after sending funds
    result = await wait_for_feather_completion(swap_result.data['id'])
    print(f"Swap completed: {result}")

# Run the swap
asyncio.run(btc_to_xmr_swap())
```

#### Two-Step Permit2 Swap

For more control, you can separate the swap initiation from monitoring:

```py
from tristero.client import TokenSpec, start_permit2_swap, wait_for_completion_with_retry
from eth_account import Account
from web3 import AsyncWeb3
from tristero.api import ChainID

# Step 1: Start the swap
account = Account.from_key(os.getenv("EVM_PRIVATE_KEY"))
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))

order_id = await start_permit2_swap(
    w3=w3,
    account=account,
    src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831"),  # USDC
    dst_t=TokenSpec(chain_id=ChainID.polygon, token_address="0xc2132D05D31c914a87C6611C10748AEb04B58e8F"),  # USDT
    raw_amount=50 * 10**6  # 50 USDC
)

print(f"Swap initiated with order ID: {order_id}")

# Step 2: Wait for completion with retry logic
result = await wait_for_completion_with_retry(order_id, feather=False)
print(f"Swap completed successfully!")
```

### 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.

### API Reference

#### Execute Full Swap
`execute_swap` handles the entire workflow automatically: quoting, signing, submitting, and monitoring.
```py
from tristero.client import execute_swap, TokenSpec
from web3 import AsyncWeb3
from eth_account.signers.local import LocalAccount

w3 = AsyncWeb3(...)  # Your Web3 provider
account: LocalAccount = ...  # Your account

result = await execute_swap(
    w3=w3,
    account=account,
    src_t=TokenSpec(chain_id=ChainID.ethereum, token_address="0xA0b8..."),
    dst_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88..."),
    raw_amount=10*(10**18),
    retry=True,
    timeout=300.0  # 5 minutes
)
```

#### Execute Permit2 Swap
`execute_permit2_swap` handles EVM-to-EVM swaps with Permit2 integration.
```py
from tristero.client import execute_permit2_swap, TokenSpec
from web3 import AsyncWeb3
from eth_account.signers.local import LocalAccount

w3 = AsyncWeb3(...)  # Your Web3 provider
account: LocalAccount = ...  # Your account

result = await execute_permit2_swap(
    w3=w3,
    account=account,
    src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589f..."),  # USDC
    dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230..."),  # USDT
    raw_amount=10*(10**6),  # 10 USDC (6 decimals)
    retry=True,
    timeout=300.0  # 5 minutes
)
```

#### Start Feather Swap
`start_feather_swap` initiates UTXO-based swaps and returns deposit information.
```py
from tristero.client import start_feather_swap, TokenSpec

swap_result = await start_feather_swap(
    src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"),
    dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"),
    dst_addr="YOUR_DESTINATION_ADDRESS",
    raw_amount=100000  # Amount in smallest unit
)

deposit_address = swap_result.deposit_address
order_id = swap_result.data['id']
```

#### Requesting a quote

`get_quote` requests a quote for a particular swap, letting you see output amounts and gas fees.

```py
from tristero.api import get_quote, ChainID

quote = await get_quote(
    from_wallet="0x1234...",           # Source wallet address
    to_wallet="0x5678...",             # Destination wallet address
    from_chain_id=ChainID.ethereum,    # Source chain
    from_address="0xA0b8...",          # Source token address (or "native")
    to_chain_id=ChainID.arbitrum,      # Destination chain
    to_address="0xaf88...",            # Destination token address (or "native")
    amount=10*(10**18),                # Amount in smallest unit (wei)
)
```

#### Creating a signed order
`create_order` creates and signs an order without submitting to be filled.

```py
from tristero.api import get_quote, ChainID

w3 = AsyncWeb3(...)  # Your Web3 provider
account: LocalAccount = ...  # Your account

data, sig = await create_order(
    w3,
    account,
    from_chain_id=ChainID.ethereum,
    from_address="0xA0b8...",
    to_chain_id=ChainID.arbitrum,
    to_address="0xaf88...",
    raw_amount=10*(10**18),
)

```

#### Submit order

`fill_order` submits a signed order for execution.

```py
from tristero.api import fill_order

data, sig = ... # from earlier

response = await fill_order(
    signature=str(sig.signature.to_0x_hex()),
    domain=data.domain.model_dump(by_alias=True, mode="json"),
    message=data.message.model_dump(by_alias=True, mode="json"),
)

order_id = response['id']
```

#### Monitoring with Built-in Functions

For convenience, use the built-in monitoring functions:

```py
from tristero.client import wait_for_completion_with_retry

# Monitor Permit2 swap with retry logic
result = await wait_for_completion_with_retry(order_id, feather=False)

# Monitor Feather swap with retry logic
result = await wait_for_completion_with_retry(order_id, feather=True)
```
