tristero 0.1.7__tar.gz → 0.2.1__tar.gz

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.
@@ -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
+ [![PyPI version](https://badge.fury.io/py/tristero.svg)](https://badge.fury.io/py/tristero)
19
+ [![Python Support](https://img.shields.io/pypi/pyversions/tristero.svg)](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,268 @@
1
+ # Tristero
2
+ [![PyPI version](https://badge.fury.io/py/tristero.svg)](https://badge.fury.io/py/tristero)
3
+ [![Python Support](https://img.shields.io/pypi/pyversions/tristero.svg)](https://pypi.org/project/tristero/)
4
+
5
+ This repository is home to Tristero's trading library.
6
+
7
+ ### Installation
8
+ ```
9
+ pip install tristero
10
+ ```
11
+
12
+ ### Quick Start
13
+
14
+ Execute a cross-chain swap in just a few lines:
15
+
16
+ ```py
17
+ import os
18
+ from tristero.client import TokenSpec, execute_swap
19
+ from eth_account import Account
20
+ from web3 import AsyncWeb3
21
+ from tristero.api import ChainID
22
+
23
+ private_key = os.getenv("EVM_PRIVATE_KEY")
24
+ account = Account.from_key(private_key)
25
+ w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))
26
+
27
+ result = await execute_swap(
28
+ w3=w3,
29
+ account=account,
30
+ src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"), # USDT
31
+ dst_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), # USDC
32
+ raw_amount=10000000 # Raw token amount (multiply by 10^decimals)
33
+ )
34
+ ```
35
+
36
+ ### Usage Examples
37
+
38
+ #### Permit2 Swap (EVM-to-EVM)
39
+
40
+ Permit2 swaps enable seamless EVM-to-EVM cross-chain token transfers with gasless approvals and EIP-712 signed orders.
41
+
42
+ ```py
43
+ import os
44
+ from tristero.client import TokenSpec, execute_permit2_swap
45
+ from eth_account import Account
46
+ from web3 import AsyncWeb3
47
+ from tristero.api import ChainID
48
+
49
+ private_key = os.getenv("EVM_PRIVATE_KEY")
50
+ account = Account.from_key(private_key)
51
+ w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://mainnet.base.org"))
52
+
53
+ # Example: USDC on Base → USDT on Avalanche
54
+ result = await execute_permit2_swap(
55
+ w3=w3,
56
+ account=account,
57
+ src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"), # USDC on Base
58
+ dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"), # USDT on Avalanche
59
+ raw_amount=10 * 10**6 # 10 USDC (6 decimals)
60
+ )
61
+
62
+ print(f"Swap completed: {result}")
63
+ ```
64
+
65
+ #### Feather Swap (UTXO-to-UTXO)
66
+
67
+ Feather swaps facilitate cross-chain transfers between UTXO-based chains like Bitcoin and Monero, providing a deposit address for manual fund transfers.
68
+
69
+ ```py
70
+ import asyncio
71
+ from tristero.client import TokenSpec, start_feather_swap, wait_for_feather_completion
72
+ from tristero.api import ChainID
73
+
74
+ # Example: Bitcoin → Monero
75
+ async def btc_to_xmr_swap():
76
+ # Start the swap to get a deposit address
77
+ swap_result = await start_feather_swap(
78
+ src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"), # BTC
79
+ dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"), # XMR
80
+ dst_addr="YOUR_XMR_RECEIVING_ADDRESS", # Your Monero destination address
81
+ raw_amount=100000 # Amount in satoshis (0.001 BTC)
82
+ )
83
+
84
+ print(f"Send {swap_result.data['amount']} satoshis to: {swap_result.deposit_address}")
85
+
86
+ # Wait for swap completion after sending funds
87
+ result = await wait_for_feather_completion(swap_result.data['id'])
88
+ print(f"Swap completed: {result}")
89
+
90
+ # Run the swap
91
+ asyncio.run(btc_to_xmr_swap())
92
+ ```
93
+
94
+ #### Two-Step Permit2 Swap
95
+
96
+ For more control, you can separate the swap initiation from monitoring:
97
+
98
+ ```py
99
+ from tristero.client import TokenSpec, start_permit2_swap, wait_for_completion_with_retry
100
+ from eth_account import Account
101
+ from web3 import AsyncWeb3
102
+ from tristero.api import ChainID
103
+
104
+ # Step 1: Start the swap
105
+ account = Account.from_key(os.getenv("EVM_PRIVATE_KEY"))
106
+ w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://arbitrum-one-rpc.publicnode.com"))
107
+
108
+ order_id = await start_permit2_swap(
109
+ w3=w3,
110
+ account=account,
111
+ src_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), # USDC
112
+ dst_t=TokenSpec(chain_id=ChainID.polygon, token_address="0xc2132D05D31c914a87C6611C10748AEb04B58e8F"), # USDT
113
+ raw_amount=50 * 10**6 # 50 USDC
114
+ )
115
+
116
+ print(f"Swap initiated with order ID: {order_id}")
117
+
118
+ # Step 2: Wait for completion with retry logic
119
+ result = await wait_for_completion_with_retry(order_id, feather=False)
120
+ print(f"Swap completed successfully!")
121
+ ```
122
+
123
+ ### How it works
124
+
125
+ Tristero supports two primary swap mechanisms:
126
+
127
+ #### Permit2 Swaps (EVM-to-EVM)
128
+ - **Quote & Approve** - Request a quote and approve tokens via Permit2 (gasless approval)
129
+ - **Sign & Submit** - Sign an EIP-712 order and submit for execution
130
+ - **Monitor** - Track swap progress via WebSocket updates
131
+
132
+ #### Feather Swaps (UTXO-based)
133
+ - **Quote & Deposit** - Request a quote to receive a deposit address
134
+ - **Manual Transfer** - Send funds to the provided deposit address
135
+ - **Monitor** - Track swap completion via WebSocket updates
136
+
137
+ This library provides both high-level convenience functions and lower-level components for precise control.
138
+
139
+ ### API Reference
140
+
141
+ #### Execute Full Swap
142
+ `execute_swap` handles the entire workflow automatically: quoting, signing, submitting, and monitoring.
143
+ ```py
144
+ from tristero.client import execute_swap, TokenSpec
145
+ from web3 import AsyncWeb3
146
+ from eth_account.signers.local import LocalAccount
147
+
148
+ w3 = AsyncWeb3(...) # Your Web3 provider
149
+ account: LocalAccount = ... # Your account
150
+
151
+ result = await execute_swap(
152
+ w3=w3,
153
+ account=account,
154
+ src_t=TokenSpec(chain_id=ChainID.ethereum, token_address="0xA0b8..."),
155
+ dst_t=TokenSpec(chain_id=ChainID.arbitrum, token_address="0xaf88..."),
156
+ raw_amount=10*(10**18),
157
+ retry=True,
158
+ timeout=300.0 # 5 minutes
159
+ )
160
+ ```
161
+
162
+ #### Execute Permit2 Swap
163
+ `execute_permit2_swap` handles EVM-to-EVM swaps with Permit2 integration.
164
+ ```py
165
+ from tristero.client import execute_permit2_swap, TokenSpec
166
+ from web3 import AsyncWeb3
167
+ from eth_account.signers.local import LocalAccount
168
+
169
+ w3 = AsyncWeb3(...) # Your Web3 provider
170
+ account: LocalAccount = ... # Your account
171
+
172
+ result = await execute_permit2_swap(
173
+ w3=w3,
174
+ account=account,
175
+ src_t=TokenSpec(chain_id=ChainID.base, token_address="0x833589f..."), # USDC
176
+ dst_t=TokenSpec(chain_id=ChainID.avalanche, token_address="0x9702230..."), # USDT
177
+ raw_amount=10*(10**6), # 10 USDC (6 decimals)
178
+ retry=True,
179
+ timeout=300.0 # 5 minutes
180
+ )
181
+ ```
182
+
183
+ #### Start Feather Swap
184
+ `start_feather_swap` initiates UTXO-based swaps and returns deposit information.
185
+ ```py
186
+ from tristero.client import start_feather_swap, TokenSpec
187
+
188
+ swap_result = await start_feather_swap(
189
+ src_t=TokenSpec(chain_id=ChainID.bitcoin, token_address="native"),
190
+ dst_t=TokenSpec(chain_id=ChainID.monero, token_address="native"),
191
+ dst_addr="YOUR_DESTINATION_ADDRESS",
192
+ raw_amount=100000 # Amount in smallest unit
193
+ )
194
+
195
+ deposit_address = swap_result.deposit_address
196
+ order_id = swap_result.data['id']
197
+ ```
198
+
199
+ #### Requesting a quote
200
+
201
+ `get_quote` requests a quote for a particular swap, letting you see output amounts and gas fees.
202
+
203
+ ```py
204
+ from tristero.api import get_quote, ChainID
205
+
206
+ quote = await get_quote(
207
+ from_wallet="0x1234...", # Source wallet address
208
+ to_wallet="0x5678...", # Destination wallet address
209
+ from_chain_id=ChainID.ethereum, # Source chain
210
+ from_address="0xA0b8...", # Source token address (or "native")
211
+ to_chain_id=ChainID.arbitrum, # Destination chain
212
+ to_address="0xaf88...", # Destination token address (or "native")
213
+ amount=10*(10**18), # Amount in smallest unit (wei)
214
+ )
215
+ ```
216
+
217
+ #### Creating a signed order
218
+ `create_order` creates and signs an order without submitting to be filled.
219
+
220
+ ```py
221
+ from tristero.api import get_quote, ChainID
222
+
223
+ w3 = AsyncWeb3(...) # Your Web3 provider
224
+ account: LocalAccount = ... # Your account
225
+
226
+ data, sig = await create_order(
227
+ w3,
228
+ account,
229
+ from_chain_id=ChainID.ethereum,
230
+ from_address="0xA0b8...",
231
+ to_chain_id=ChainID.arbitrum,
232
+ to_address="0xaf88...",
233
+ raw_amount=10*(10**18),
234
+ )
235
+
236
+ ```
237
+
238
+ #### Submit order
239
+
240
+ `fill_order` submits a signed order for execution.
241
+
242
+ ```py
243
+ from tristero.api import fill_order
244
+
245
+ data, sig = ... # from earlier
246
+
247
+ response = await fill_order(
248
+ signature=str(sig.signature.to_0x_hex()),
249
+ domain=data.domain.model_dump(by_alias=True, mode="json"),
250
+ message=data.message.model_dump(by_alias=True, mode="json"),
251
+ )
252
+
253
+ order_id = response['id']
254
+ ```
255
+
256
+ #### Monitoring with Built-in Functions
257
+
258
+ For convenience, use the built-in monitoring functions:
259
+
260
+ ```py
261
+ from tristero.client import wait_for_completion_with_retry
262
+
263
+ # Monitor Permit2 swap with retry logic
264
+ result = await wait_for_completion_with_retry(order_id, feather=False)
265
+
266
+ # Monitor Feather swap with retry logic
267
+ result = await wait_for_completion_with_retry(order_id, feather=True)
268
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tristero"
3
- version = "0.1.7"
3
+ version = "0.2.1"
4
4
  description = "Library for trading on Tristero"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -9,11 +9,12 @@ authors = [
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
11
11
  "eth-account>=0.8.0",
12
+ "glom>=25.12.0",
12
13
  "httpx>=0.23.0",
13
14
  "pydantic>=2.0.0",
14
15
  "tenacity>=8.0.0",
15
16
  "web3>=6.0.0",
16
- "websockets>=10.0"
17
+ "websockets>=10.0",
17
18
  ]
18
19
 
19
20
  [build-system]
@@ -0,0 +1,18 @@
1
+ from tristero.client import OrderFailedException, StuckException, SwapException, TokenSpec, execute_permit2_swap, start_permit2_swap, start_feather_swap, wait_for_feather_completion, wait_for_completion, wait_for_completion_with_retry
2
+ from tristero.config import set_config
3
+ from tristero.data import ChainID
4
+
5
+ __all__ = [
6
+ "ChainID",
7
+ "TokenSpec",
8
+ "StuckException",
9
+ "OrderFailedException",
10
+ "SwapException",
11
+ "start_permit2_swap",
12
+ "start_feather_swap",
13
+ "execute_permit2_swap",
14
+ "wait_for_feather_completion",
15
+ "wait_for_completion",
16
+ "wait_for_completion_with_retry",
17
+ "set_config",
18
+ ]
@@ -0,0 +1,104 @@
1
+ from typing import Any, Optional
2
+ import httpx
3
+ from websockets.asyncio.client import connect
4
+ from tristero.config import get_config
5
+ from tristero.data import get_gas_addr
6
+
7
+ class APIException(Exception):
8
+ pass
9
+
10
+ class QuoteException(Exception):
11
+ pass
12
+
13
+ def handle_resp(resp: httpx.Response):
14
+ try:
15
+ resp.raise_for_status()
16
+ data = resp.json()
17
+ return data
18
+ except Exception as e:
19
+ try:
20
+ data = resp.json()
21
+ except Exception as json_e:
22
+ if resp.text:
23
+ raise APIException(resp.text)
24
+ else:
25
+ raise e
26
+ raise APIException(data)
27
+
28
+ async def t_get(client: httpx.AsyncClient, url: str):
29
+ resp = await client.get(url, headers=get_config().headers, timeout=20)
30
+ return handle_resp(resp)
31
+
32
+
33
+ async def t_post(client: httpx.AsyncClient, url: str, body: Optional[dict[str, Any]]):
34
+ resp = await client.post(url, headers=get_config().headers, json=body, timeout=20)
35
+ return handle_resp(resp)
36
+
37
+ def or_native(chain_id: str, address: str):
38
+ return get_gas_addr(chain_id) if address == "native" else address
39
+
40
+ c = httpx.AsyncClient()
41
+
42
+ async def get_quote(
43
+ from_wallet: str,
44
+ to_wallet: str,
45
+ from_chain_id: str,
46
+ from_address: str,
47
+ to_chain_id: str,
48
+ to_address: str,
49
+ amount: int,
50
+ ):
51
+ from_chain_id = from_chain_id
52
+ to_chain_id = to_chain_id
53
+
54
+ from_token_address = or_native(from_chain_id, from_address)
55
+ to_token_address = or_native(to_chain_id, to_address)
56
+
57
+ data = {
58
+ "src_chain_id": from_chain_id,
59
+ "src_token_address": from_token_address,
60
+ "src_token_quantity": str(int(amount)),
61
+ "src_wallet_address": from_wallet,
62
+ "dst_chain_id": to_chain_id,
63
+ "dst_token_address": to_token_address,
64
+ "dst_wallet_address": to_wallet,
65
+ }
66
+ resp = await c.post(
67
+ get_config().quoter_url,
68
+ json=data,
69
+ )
70
+ # print(data, resp)
71
+ try:
72
+ return handle_resp(resp)
73
+ except Exception as e:
74
+ raise QuoteException(e, data) from e
75
+
76
+ async def fill_order(signature: str, domain: dict[str, Any], message: dict[str, Any]):
77
+ data = {"signature": signature, "domain": domain, "message": message}
78
+ resp = await c.post(
79
+ get_config().filler_url,
80
+ json=data,
81
+ )
82
+ return handle_resp(resp)
83
+
84
+ async def fill_order_feather(src_chain: str, dst_chain: str, dst_address: str, amount: int, client_id: str = ''):
85
+ data = {
86
+ "client_id": client_id,
87
+ "src_chain": src_chain,
88
+ "dst_chain": dst_chain,
89
+ "dst_address": dst_address,
90
+ "amount": amount
91
+ }
92
+ resp = await c.post(
93
+ get_config().filler_url,
94
+ json=data,
95
+ )
96
+ return handle_resp(resp)
97
+
98
+ async def poll_updates(order_id: str):
99
+ ws = await connect(f"{get_config().ws_url}/{order_id}")
100
+ return ws
101
+
102
+ async def poll_updates_feather(order_id: str):
103
+ ws = await connect(f"{get_config().ws_url}/feather/{order_id}")
104
+ return ws