tristero 0.1.7__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tristero/__init__.py +9 -3
- tristero/api.py +48 -102
- tristero/client.py +88 -43
- tristero/data.py +35 -0
- tristero/files/chains.json +6557 -0
- tristero/permit2.py +30 -27
- tristero-0.2.1.dist-info/METADATA +284 -0
- tristero-0.2.1.dist-info/RECORD +14 -0
- tristero-0.1.7.dist-info/METADATA +0 -157
- tristero-0.1.7.dist-info/RECORD +0 -12
- {tristero-0.1.7.dist-info → tristero-0.2.1.dist-info}/WHEEL +0 -0
tristero/permit2.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
1
2
|
import logging
|
|
2
|
-
from typing import Any, List, Optional, TypeVar, cast
|
|
3
|
+
from typing import Any, List, Literal, Optional, TypeVar, Union, cast
|
|
3
4
|
from eth_account import Account
|
|
4
5
|
from eth_account.datastructures import SignedMessage, SignedTransaction
|
|
5
6
|
from eth_account.signers.base import BaseAccount
|
|
@@ -8,11 +9,7 @@ from eth_account.types import TransactionDictType
|
|
|
8
9
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
9
10
|
from pydantic.alias_generators import to_camel
|
|
10
11
|
from web3 import AsyncBaseProvider, AsyncWeb3
|
|
11
|
-
from eth_typing import Address, ChecksumAddress
|
|
12
|
-
import time
|
|
13
|
-
import math
|
|
14
12
|
import random
|
|
15
|
-
import web3
|
|
16
13
|
from web3.contract import AsyncContract
|
|
17
14
|
from functools import cache, lru_cache
|
|
18
15
|
import json
|
|
@@ -22,11 +19,11 @@ from importlib import resources as impresources
|
|
|
22
19
|
from web3 import Web3
|
|
23
20
|
from web3.eth import AsyncEth
|
|
24
21
|
|
|
25
|
-
from .api import
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
from tristero.api import get_quote
|
|
23
|
+
|
|
24
|
+
from .data import (
|
|
25
|
+
get_permit2_addr,
|
|
26
|
+
get_wrapped_gas_addr,
|
|
30
27
|
)
|
|
31
28
|
|
|
32
29
|
logger = logging.getLogger(__name__)
|
|
@@ -35,10 +32,10 @@ P = TypeVar("P", bound=AsyncBaseProvider)
|
|
|
35
32
|
|
|
36
33
|
PERMIT2_ABI_FILE = impresources.files("tristero.files") / "permit2_abi.json"
|
|
37
34
|
ERC20_ABI_FILE = impresources.files("tristero.files") / "erc20_abi.json"
|
|
35
|
+
|
|
38
36
|
PERMIT2_ABI = json.loads(PERMIT2_ABI_FILE.read_text())
|
|
39
37
|
ERC20_ABI = json.loads(ERC20_ABI_FILE.read_text())
|
|
40
38
|
|
|
41
|
-
|
|
42
39
|
@lru_cache(maxsize=None)
|
|
43
40
|
def get_permit2(eth: AsyncEth, permit2_address: str):
|
|
44
41
|
return eth.contract(
|
|
@@ -183,13 +180,13 @@ async def prepare_data_for_signature(
|
|
|
183
180
|
if not quote.order_data.deadline:
|
|
184
181
|
raise ValueError("Deadline is required in the order_data")
|
|
185
182
|
|
|
186
|
-
from_chain =
|
|
183
|
+
from_chain = str(sell_data.chain_id)
|
|
187
184
|
|
|
188
185
|
deadline = quote.order_data.deadline
|
|
189
186
|
|
|
190
187
|
# Handle native token address conversion
|
|
191
188
|
token_address = (
|
|
192
|
-
|
|
189
|
+
get_wrapped_gas_addr(from_chain)
|
|
193
190
|
if sell_data.token.address == "native"
|
|
194
191
|
else sell_data.token.address
|
|
195
192
|
)
|
|
@@ -213,7 +210,7 @@ async def prepare_data_for_signature(
|
|
|
213
210
|
)
|
|
214
211
|
|
|
215
212
|
# Get Permit2 address
|
|
216
|
-
permit2_address =
|
|
213
|
+
permit2_address = get_permit2_addr(from_chain)
|
|
217
214
|
if not permit2_address:
|
|
218
215
|
raise ValueError("Permit2 not deployed on this chain.")
|
|
219
216
|
|
|
@@ -322,7 +319,7 @@ async def sign_permit2(
|
|
|
322
319
|
async def approve_permit2(
|
|
323
320
|
w3: AsyncWeb3[P],
|
|
324
321
|
account: LocalAccount,
|
|
325
|
-
chain:
|
|
322
|
+
chain: str,
|
|
326
323
|
token_address: str,
|
|
327
324
|
required_quantity: int,
|
|
328
325
|
maxGas: int = 100000,
|
|
@@ -330,7 +327,7 @@ async def approve_permit2(
|
|
|
330
327
|
wallet_address = account.address
|
|
331
328
|
|
|
332
329
|
erc20 = get_erc20_contract(w3, token_address)
|
|
333
|
-
permit2_contract =
|
|
330
|
+
permit2_contract = get_permit2(chain)
|
|
334
331
|
current_allowance = await erc20.functions.allowance(
|
|
335
332
|
wallet_address, permit2_contract
|
|
336
333
|
).call()
|
|
@@ -358,28 +355,34 @@ async def approve_permit2(
|
|
|
358
355
|
logger.debug(f"→ Approval tx hash: {tx_hash.hex()}")
|
|
359
356
|
return tx_hash.hex()
|
|
360
357
|
|
|
358
|
+
@dataclass
|
|
359
|
+
class Permit2Order:
|
|
360
|
+
msg: SignatureData
|
|
361
|
+
sig: SignedMessage
|
|
361
362
|
|
|
362
|
-
async def
|
|
363
|
+
async def create_permit2_order(
|
|
363
364
|
w3: AsyncWeb3[P],
|
|
364
365
|
account: LocalAccount,
|
|
365
|
-
src_chain:
|
|
366
|
+
src_chain: str,
|
|
366
367
|
src_token: str,
|
|
367
|
-
dst_chain:
|
|
368
|
+
dst_chain: str,
|
|
368
369
|
dst_token: str,
|
|
369
370
|
raw_amount: int,
|
|
370
|
-
|
|
371
|
-
):
|
|
372
|
-
if not
|
|
373
|
-
|
|
371
|
+
dst_address: str | None = None,
|
|
372
|
+
) -> Permit2Order:
|
|
373
|
+
if not dst_address:
|
|
374
|
+
dst_address = account.address
|
|
374
375
|
q = await get_quote(
|
|
375
376
|
account.address,
|
|
376
|
-
|
|
377
|
+
dst_address,
|
|
377
378
|
src_chain,
|
|
378
379
|
src_token,
|
|
379
380
|
dst_chain,
|
|
380
381
|
dst_token,
|
|
381
382
|
raw_amount,
|
|
382
383
|
)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
if q['orderType'] == 'FEATHER':
|
|
385
|
+
raise Exception('Feather routes unsupported for automatic trades')
|
|
386
|
+
_ = await approve_permit2(w3, account, src_chain, src_token, raw_amount)
|
|
387
|
+
msg, sig = await sign_permit2(w3.eth, account, account.address, raw_amount, q)
|
|
388
|
+
return Permit2Order(msg, sig)
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: tristero
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Library for trading on Tristero
|
|
5
|
+
Author: pty1
|
|
6
|
+
Author-email: pty1 <pty11@proton.me>
|
|
7
|
+
Requires-Dist: eth-account>=0.8.0
|
|
8
|
+
Requires-Dist: glom>=25.12.0
|
|
9
|
+
Requires-Dist: httpx>=0.23.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
|
11
|
+
Requires-Dist: tenacity>=8.0.0
|
|
12
|
+
Requires-Dist: web3>=6.0.0
|
|
13
|
+
Requires-Dist: websockets>=10.0
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Tristero
|
|
18
|
+
[](https://badge.fury.io/py/tristero)
|
|
19
|
+
[](https://pypi.org/project/tristero/)
|
|
20
|
+
|
|
21
|
+
This repository is home to Tristero's trading library.
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
```
|
|
25
|
+
pip install tristero
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Quick Start
|
|
29
|
+
|
|
30
|
+
Execute a cross-chain swap in just a few lines:
|
|
31
|
+
|
|
32
|
+
```py
|
|
33
|
+
import os
|
|
34
|
+
from tristero.client import TokenSpec, execute_swap
|
|
35
|
+
from eth_account import Account
|
|
36
|
+
from web3 import AsyncWeb3
|
|
37
|
+
from tristero.api import ChainID
|
|
38
|
+
|
|
39
|
+
private_key = os.getenv("EVM_PRIVATE_KEY")
|
|
40
|
+
account = Account.from_key(private_key)
|
|
41
|
+
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))
|
|
42
|
+
|
|
43
|
+
result = await execute_swap(
|
|
44
|
+
w3=w3,
|
|
45
|
+
account=account,
|
|
46
|
+
src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), # USDT
|
|
47
|
+
dst_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), # USDC
|
|
48
|
+
raw_amount=10000000 # Raw token amount (multiply by 10^decimals)
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Usage Examples
|
|
53
|
+
|
|
54
|
+
#### Permit2 Swap (EVM-to-EVM)
|
|
55
|
+
|
|
56
|
+
Permit2 swaps enable seamless EVM-to-EVM cross-chain token transfers with gasless approvals and EIP-712 signed orders.
|
|
57
|
+
|
|
58
|
+
```py
|
|
59
|
+
import os
|
|
60
|
+
from tristero.client import TokenSpec, execute_permit2_swap
|
|
61
|
+
from eth_account import Account
|
|
62
|
+
from web3 import AsyncWeb3
|
|
63
|
+
from tristero.api import ChainID
|
|
64
|
+
|
|
65
|
+
private_key = os.getenv("EVM_PRIVATE_KEY")
|
|
66
|
+
account = Account.from_key(private_key)
|
|
67
|
+
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://mainnet.base.org"))
|
|
68
|
+
|
|
69
|
+
# Example: USDC on Base → USDT on Avalanche
|
|
70
|
+
result = await execute_permit2_swap(
|
|
71
|
+
w3=w3,
|
|
72
|
+
account=account,
|
|
73
|
+
src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), # USDC on Base
|
|
74
|
+
dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"), # USDT on Avalanche
|
|
75
|
+
raw_amount=10 * 10**6 # 10 USDC (6 decimals)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
print(f"Swap completed: {result}")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### Feather Swap (UTXO-to-UTXO)
|
|
82
|
+
|
|
83
|
+
Feather swaps facilitate cross-chain transfers between UTXO-based chains like Bitcoin and Monero, providing a deposit address for manual fund transfers.
|
|
84
|
+
|
|
85
|
+
```py
|
|
86
|
+
import asyncio
|
|
87
|
+
from tristero.client import TokenSpec, start_feather_swap, wait_for_feather_completion
|
|
88
|
+
from tristero.api import ChainID
|
|
89
|
+
|
|
90
|
+
# Example: Bitcoin → Monero
|
|
91
|
+
async def btc_to_xmr_swap():
|
|
92
|
+
# Start the swap to get a deposit address
|
|
93
|
+
swap_result = await start_feather_swap(
|
|
94
|
+
src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"), # BTC
|
|
95
|
+
dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"), # XMR
|
|
96
|
+
dst_addr="YOUR_XMR_RECEIVING_ADDRESS", # Your Monero destination address
|
|
97
|
+
raw_amount=100000 # Amount in satoshis (0.001 BTC)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
print(f"Send {swap_result.data['amount']} satoshis to: {swap_result.deposit_address}")
|
|
101
|
+
|
|
102
|
+
# Wait for swap completion after sending funds
|
|
103
|
+
result = await wait_for_feather_completion(swap_result.data['id'])
|
|
104
|
+
print(f"Swap completed: {result}")
|
|
105
|
+
|
|
106
|
+
# Run the swap
|
|
107
|
+
asyncio.run(btc_to_xmr_swap())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Two-Step Permit2 Swap
|
|
111
|
+
|
|
112
|
+
For more control, you can separate the swap initiation from monitoring:
|
|
113
|
+
|
|
114
|
+
```py
|
|
115
|
+
from tristero.client import TokenSpec, start_permit2_swap, wait_for_completion_with_retry
|
|
116
|
+
from eth_account import Account
|
|
117
|
+
from web3 import AsyncWeb3
|
|
118
|
+
from tristero.api import ChainID
|
|
119
|
+
|
|
120
|
+
# Step 1: Start the swap
|
|
121
|
+
account = Account.from_key(os.getenv("EVM_PRIVATE_KEY"))
|
|
122
|
+
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))
|
|
123
|
+
|
|
124
|
+
order_id = await start_permit2_swap(
|
|
125
|
+
w3=w3,
|
|
126
|
+
account=account,
|
|
127
|
+
src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), # USDC
|
|
128
|
+
dst_t=TokenSpec(chain_id=ChainID.polygon, token_address="0xc2132D05D31c914a87C6611C10748AEb04B58e8F"), # USDT
|
|
129
|
+
raw_amount=50 * 10**6 # 50 USDC
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
print(f"Swap initiated with order ID: {order_id}")
|
|
133
|
+
|
|
134
|
+
# Step 2: Wait for completion with retry logic
|
|
135
|
+
result = await wait_for_completion_with_retry(order_id, feather=False)
|
|
136
|
+
print(f"Swap completed successfully!")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### How it works
|
|
140
|
+
|
|
141
|
+
Tristero supports two primary swap mechanisms:
|
|
142
|
+
|
|
143
|
+
#### Permit2 Swaps (EVM-to-EVM)
|
|
144
|
+
- **Quote & Approve** - Request a quote and approve tokens via Permit2 (gasless approval)
|
|
145
|
+
- **Sign & Submit** - Sign an EIP-712 order and submit for execution
|
|
146
|
+
- **Monitor** - Track swap progress via WebSocket updates
|
|
147
|
+
|
|
148
|
+
#### Feather Swaps (UTXO-based)
|
|
149
|
+
- **Quote & Deposit** - Request a quote to receive a deposit address
|
|
150
|
+
- **Manual Transfer** - Send funds to the provided deposit address
|
|
151
|
+
- **Monitor** - Track swap completion via WebSocket updates
|
|
152
|
+
|
|
153
|
+
This library provides both high-level convenience functions and lower-level components for precise control.
|
|
154
|
+
|
|
155
|
+
### API Reference
|
|
156
|
+
|
|
157
|
+
#### Execute Full Swap
|
|
158
|
+
`execute_swap` handles the entire workflow automatically: quoting, signing, submitting, and monitoring.
|
|
159
|
+
```py
|
|
160
|
+
from tristero.client import execute_swap, TokenSpec
|
|
161
|
+
from web3 import AsyncWeb3
|
|
162
|
+
from eth_account.signers.local import LocalAccount
|
|
163
|
+
|
|
164
|
+
w3 = AsyncWeb3(...) # Your Web3 provider
|
|
165
|
+
account: LocalAccount = ... # Your account
|
|
166
|
+
|
|
167
|
+
result = await execute_swap(
|
|
168
|
+
w3=w3,
|
|
169
|
+
account=account,
|
|
170
|
+
src_t=TokenSpec(chain_id=ChainID.ethereum, token_address="0xA0b8..."),
|
|
171
|
+
dst_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88..."),
|
|
172
|
+
raw_amount=10*(10**18),
|
|
173
|
+
retry=True,
|
|
174
|
+
timeout=300.0 # 5 minutes
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Execute Permit2 Swap
|
|
179
|
+
`execute_permit2_swap` handles EVM-to-EVM swaps with Permit2 integration.
|
|
180
|
+
```py
|
|
181
|
+
from tristero.client import execute_permit2_swap, TokenSpec
|
|
182
|
+
from web3 import AsyncWeb3
|
|
183
|
+
from eth_account.signers.local import LocalAccount
|
|
184
|
+
|
|
185
|
+
w3 = AsyncWeb3(...) # Your Web3 provider
|
|
186
|
+
account: LocalAccount = ... # Your account
|
|
187
|
+
|
|
188
|
+
result = await execute_permit2_swap(
|
|
189
|
+
w3=w3,
|
|
190
|
+
account=account,
|
|
191
|
+
src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589f..."), # USDC
|
|
192
|
+
dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230..."), # USDT
|
|
193
|
+
raw_amount=10*(10**6), # 10 USDC (6 decimals)
|
|
194
|
+
retry=True,
|
|
195
|
+
timeout=300.0 # 5 minutes
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Start Feather Swap
|
|
200
|
+
`start_feather_swap` initiates UTXO-based swaps and returns deposit information.
|
|
201
|
+
```py
|
|
202
|
+
from tristero.client import start_feather_swap, TokenSpec
|
|
203
|
+
|
|
204
|
+
swap_result = await start_feather_swap(
|
|
205
|
+
src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"),
|
|
206
|
+
dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"),
|
|
207
|
+
dst_addr="YOUR_DESTINATION_ADDRESS",
|
|
208
|
+
raw_amount=100000 # Amount in smallest unit
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
deposit_address = swap_result.deposit_address
|
|
212
|
+
order_id = swap_result.data['id']
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Requesting a quote
|
|
216
|
+
|
|
217
|
+
`get_quote` requests a quote for a particular swap, letting you see output amounts and gas fees.
|
|
218
|
+
|
|
219
|
+
```py
|
|
220
|
+
from tristero.api import get_quote, ChainID
|
|
221
|
+
|
|
222
|
+
quote = await get_quote(
|
|
223
|
+
from_wallet="0x1234...", # Source wallet address
|
|
224
|
+
to_wallet="0x5678...", # Destination wallet address
|
|
225
|
+
from_chain_id=ChainID.ethereum, # Source chain
|
|
226
|
+
from_address="0xA0b8...", # Source token address (or "native")
|
|
227
|
+
to_chain_id=ChainID.arbitrum, # Destination chain
|
|
228
|
+
to_address="0xaf88...", # Destination token address (or "native")
|
|
229
|
+
amount=10*(10**18), # Amount in smallest unit (wei)
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### Creating a signed order
|
|
234
|
+
`create_order` creates and signs an order without submitting to be filled.
|
|
235
|
+
|
|
236
|
+
```py
|
|
237
|
+
from tristero.api import get_quote, ChainID
|
|
238
|
+
|
|
239
|
+
w3 = AsyncWeb3(...) # Your Web3 provider
|
|
240
|
+
account: LocalAccount = ... # Your account
|
|
241
|
+
|
|
242
|
+
data, sig = await create_order(
|
|
243
|
+
w3,
|
|
244
|
+
account,
|
|
245
|
+
from_chain_id=ChainID.ethereum,
|
|
246
|
+
from_address="0xA0b8...",
|
|
247
|
+
to_chain_id=ChainID.arbitrum,
|
|
248
|
+
to_address="0xaf88...",
|
|
249
|
+
raw_amount=10*(10**18),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### Submit order
|
|
255
|
+
|
|
256
|
+
`fill_order` submits a signed order for execution.
|
|
257
|
+
|
|
258
|
+
```py
|
|
259
|
+
from tristero.api import fill_order
|
|
260
|
+
|
|
261
|
+
data, sig = ... # from earlier
|
|
262
|
+
|
|
263
|
+
response = await fill_order(
|
|
264
|
+
signature=str(sig.signature.to_0x_hex()),
|
|
265
|
+
domain=data.domain.model_dump(by_alias=True, mode="json"),
|
|
266
|
+
message=data.message.model_dump(by_alias=True, mode="json"),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
order_id = response['id']
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Monitoring with Built-in Functions
|
|
273
|
+
|
|
274
|
+
For convenience, use the built-in monitoring functions:
|
|
275
|
+
|
|
276
|
+
```py
|
|
277
|
+
from tristero.client import wait_for_completion_with_retry
|
|
278
|
+
|
|
279
|
+
# Monitor Permit2 swap with retry logic
|
|
280
|
+
result = await wait_for_completion_with_retry(order_id, feather=False)
|
|
281
|
+
|
|
282
|
+
# Monitor Feather swap with retry logic
|
|
283
|
+
result = await wait_for_completion_with_retry(order_id, feather=True)
|
|
284
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
tristero/__init__.py,sha256=hMbay3U8ovonIAH4N45Tx_U8q1MrTkS5qLrquWz_19g,624
|
|
2
|
+
tristero/api.py,sha256=IuCxik9GSaekWPBuaCFalYzrVQWpliS8MO8dJn2115c,2967
|
|
3
|
+
tristero/client.py,sha256=BkQBxFjyQ13JSHVOYlodCMbQGXYK2a-GHxQdI_6HEso,5673
|
|
4
|
+
tristero/config.py,sha256=_0PP2gufvq_t-gdKqDrxht2BxoWpNiGXk4m_u57WOYY,530
|
|
5
|
+
tristero/data.py,sha256=gKqoLOg2KQu3shqtHg-AS4kDeb7XOFMy76sIbNBIw1w,887
|
|
6
|
+
tristero/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
tristero/files/chains.json,sha256=04jFywe_V0wrS0tcjdCUHMxIutaLJ_k5ABbbmkyZ9jY,256150
|
|
8
|
+
tristero/files/erc20_abi.json,sha256=jvsJ6aCwhMcmo3Yy1ajt5lPl_nTRg7tv-tGj87xzTOg,12800
|
|
9
|
+
tristero/files/permit2_abi.json,sha256=NV0AUUA9kqFPk56njvRRzUyjBhrBncKIMd3PrSH0LCc,17817
|
|
10
|
+
tristero/permit2.py,sha256=CFj6DQPC8UqCcI0HH8PJHJYk8Hba-Aspokxk229R29I,11517
|
|
11
|
+
tristero/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
tristero-0.2.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
13
|
+
tristero-0.2.1.dist-info/METADATA,sha256=ysDNK9nnh0L5gcEFD5wgc9nbsGcASVST1RpmLe-mzJc,9007
|
|
14
|
+
tristero-0.2.1.dist-info/RECORD,,
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: tristero
|
|
3
|
-
Version: 0.1.7
|
|
4
|
-
Summary: Library for trading on Tristero
|
|
5
|
-
Author: pty1
|
|
6
|
-
Author-email: pty1 <pty11@proton.me>
|
|
7
|
-
Requires-Dist: eth-account>=0.8.0
|
|
8
|
-
Requires-Dist: httpx>=0.23.0
|
|
9
|
-
Requires-Dist: pydantic>=2.0.0
|
|
10
|
-
Requires-Dist: tenacity>=8.0.0
|
|
11
|
-
Requires-Dist: web3>=6.0.0
|
|
12
|
-
Requires-Dist: websockets>=10.0
|
|
13
|
-
Requires-Python: >=3.10
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
|
|
16
|
-
# Tristero
|
|
17
|
-
[](https://badge.fury.io/py/tristero)
|
|
18
|
-
[](https://pypi.org/project/tristero/)
|
|
19
|
-
|
|
20
|
-
This repository is home to Tristero's trading library.
|
|
21
|
-
|
|
22
|
-
### Installation
|
|
23
|
-
```
|
|
24
|
-
pip install tristero
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Quick Start
|
|
28
|
-
|
|
29
|
-
Execute a cross-chain swap in just a few lines:
|
|
30
|
-
|
|
31
|
-
```py
|
|
32
|
-
import os
|
|
33
|
-
from tristero.client import TokenSpec, execute_swap
|
|
34
|
-
from eth_account import Account
|
|
35
|
-
from web3 import AsyncWeb3
|
|
36
|
-
from tristero.api import ChainID
|
|
37
|
-
|
|
38
|
-
private_key = os.getenv("EVM_PRIVATE_KEY")
|
|
39
|
-
account = Account.from_key(private_key)
|
|
40
|
-
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))
|
|
41
|
-
|
|
42
|
-
result = await execute_swap(
|
|
43
|
-
w3=w3,
|
|
44
|
-
account=account,
|
|
45
|
-
src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), # USDT
|
|
46
|
-
dst_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), # USDC
|
|
47
|
-
raw_amount=10000000 # Raw token amount (multiply by 10^decimals)
|
|
48
|
-
)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### How it works
|
|
52
|
-
|
|
53
|
-
Tristero swaps happen in two steps:
|
|
54
|
-
- **Quote & Sign** - Request a quote from the server and sign it with your private key
|
|
55
|
-
- **Submit & Fill** - Submit the signed order to be filled at a later date
|
|
56
|
-
|
|
57
|
-
This library provides both high-level convenience functions and lower-level components for precise control.
|
|
58
|
-
|
|
59
|
-
### API Reference
|
|
60
|
-
|
|
61
|
-
#### Execute Full Swap
|
|
62
|
-
`execute_swap` handles the entire workflow automatically: quoting, signing, submitting, and monitoring.
|
|
63
|
-
```py
|
|
64
|
-
from tristero.swap import execute_swap, TokenSpec
|
|
65
|
-
from web3 import AsyncWeb3
|
|
66
|
-
from eth_account.signers.local import LocalAccount
|
|
67
|
-
|
|
68
|
-
w3 = AsyncWeb3(...) # Your Web3 provider
|
|
69
|
-
account: LocalAccount = ... # Your account
|
|
70
|
-
|
|
71
|
-
result = await execute_swap(
|
|
72
|
-
w3=w3,
|
|
73
|
-
account=account,
|
|
74
|
-
src_t=TokenSpec(chain_id=ChainID.ethereum, token_address="0xA0b8..."),
|
|
75
|
-
dst_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88..."),
|
|
76
|
-
raw_amount=10*(10**18),
|
|
77
|
-
retry=True,
|
|
78
|
-
timeout=300.0 # 5 minutes
|
|
79
|
-
)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
#### Requesting a quote
|
|
83
|
-
|
|
84
|
-
`get_quote` requests a quote for a particular swap, letting you see output amounts and gas fees.
|
|
85
|
-
|
|
86
|
-
```py
|
|
87
|
-
from tristero.api import get_quote, ChainID
|
|
88
|
-
|
|
89
|
-
quote = await get_quote(
|
|
90
|
-
from_wallet="0x1234...", # Source wallet address
|
|
91
|
-
to_wallet="0x5678...", # Destination wallet address
|
|
92
|
-
from_chain_id=ChainID.ethereum, # Source chain
|
|
93
|
-
from_address="0xA0b8...", # Source token address (or "native")
|
|
94
|
-
to_chain_id=ChainID.arbitrum, # Destination chain
|
|
95
|
-
to_address="0xaf88...", # Destination token address (or "native")
|
|
96
|
-
amount=10*(10**18), # Amount in smallest unit (wei)
|
|
97
|
-
)
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### Creating a signed order
|
|
101
|
-
`create_order` creates and signs an order without submitting to be filled.
|
|
102
|
-
|
|
103
|
-
```py
|
|
104
|
-
from tristero.api import get_quote, ChainID
|
|
105
|
-
|
|
106
|
-
w3 = AsyncWeb3(...) # Your Web3 provider
|
|
107
|
-
account: LocalAccount = ... # Your account
|
|
108
|
-
|
|
109
|
-
data, sig = await create_order(
|
|
110
|
-
w3,
|
|
111
|
-
account,
|
|
112
|
-
from_chain_id=ChainID.ethereum,
|
|
113
|
-
from_address="0xA0b8...",
|
|
114
|
-
to_chain_id=ChainID.arbitrum,
|
|
115
|
-
to_address="0xaf88...",
|
|
116
|
-
raw_amount=10*(10**18),
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
#### Submit order
|
|
122
|
-
|
|
123
|
-
`fill_order` submits a signed order for execution.
|
|
124
|
-
|
|
125
|
-
```py
|
|
126
|
-
from tristero.api import fill_order
|
|
127
|
-
|
|
128
|
-
data, sig = ... # from earlier
|
|
129
|
-
|
|
130
|
-
response = await fill_order(
|
|
131
|
-
signature=str(sig.signature.to_0x_hex()),
|
|
132
|
-
domain=data.domain.model_dump(by_alias=True, mode="json"),
|
|
133
|
-
message=data.message.model_dump(by_alias=True, mode="json"),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
order_id = response['id']
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
#### Subscribing for updates
|
|
140
|
-
|
|
141
|
-
Orders can be monitored for changes and status live
|
|
142
|
-
|
|
143
|
-
```py
|
|
144
|
-
from tristero.api import poll_updates
|
|
145
|
-
import json
|
|
146
|
-
|
|
147
|
-
ws = await poll_updates(order_id)
|
|
148
|
-
|
|
149
|
-
async for msg in ws:
|
|
150
|
-
update = json.loads(msg)
|
|
151
|
-
print(f"Completed: {update['completed']}")
|
|
152
|
-
print(f"Failed: {update['failed']}")
|
|
153
|
-
|
|
154
|
-
if update["completed"] or update["failed"]:
|
|
155
|
-
await ws.close()
|
|
156
|
-
break
|
|
157
|
-
```
|
tristero-0.1.7.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
tristero/__init__.py,sha256=Jmbdg3LwOD4rrO0eerQTmn-wO-Sr4xRZ2r_RtM4iNDg,315
|
|
2
|
-
tristero/api.py,sha256=E5CyEbEZjeEpptJ2C5570g-ZD02PWE73USTz5v-uKss,5520
|
|
3
|
-
tristero/client.py,sha256=nro5d9z5cAZOWSgF3s43UcFPMrTqbFk4DxIWo14u7-Y,4086
|
|
4
|
-
tristero/config.py,sha256=_0PP2gufvq_t-gdKqDrxht2BxoWpNiGXk4m_u57WOYY,530
|
|
5
|
-
tristero/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
tristero/files/erc20_abi.json,sha256=jvsJ6aCwhMcmo3Yy1ajt5lPl_nTRg7tv-tGj87xzTOg,12800
|
|
7
|
-
tristero/files/permit2_abi.json,sha256=NV0AUUA9kqFPk56njvRRzUyjBhrBncKIMd3PrSH0LCc,17817
|
|
8
|
-
tristero/permit2.py,sha256=eBSAYTnnDI2NmOJDx5Px5ae7bSjE_FEmkZZn9wx-uZU,11396
|
|
9
|
-
tristero/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
tristero-0.1.7.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
11
|
-
tristero-0.1.7.dist-info/METADATA,sha256=nC7bzXMgELN7Ln80LSu4ArqLGB1YIRSyrHGBjSIpybA,4384
|
|
12
|
-
tristero-0.1.7.dist-info/RECORD,,
|
|
File without changes
|