cryptointerface 0.1.0__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.
@@ -0,0 +1,264 @@
1
+ import requests
2
+ import polars as pl
3
+ from web3 import Web3
4
+ from .periphery.db import _init_tables, insert_data
5
+ from .periphery.mapping import COINGECKO_CHAIN_IDS
6
+
7
+ _ERC20_APPROVE_ABI = [
8
+ {
9
+ "inputs": [
10
+ {"internalType": "address", "name": "spender", "type": "address"},
11
+ {"internalType": "uint256", "name": "amount", "type": "uint256"},
12
+ ],
13
+ "name": "approve",
14
+ "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
15
+ "stateMutability": "nonpayable",
16
+ "type": "function",
17
+ }
18
+ ]
19
+
20
+ _ERC20_ALLOWANCE_ABI = [
21
+ {
22
+ "inputs": [
23
+ {"internalType": "address", "name": "owner", "type": "address"},
24
+ {"internalType": "address", "name": "spender", "type": "address"},
25
+ ],
26
+ "name": "allowance",
27
+ "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
28
+ "stateMutability": "view",
29
+ "type": "function",
30
+ }
31
+ ]
32
+
33
+
34
+ class Token:
35
+ def __init__(self, symbol: str, chain_id: str):
36
+ self.symbol = symbol
37
+ self.chain_id = chain_id
38
+ self.interface = TokenInterface()
39
+
40
+ self._address = None
41
+ self._decimals = None
42
+ self._info = None
43
+
44
+ @property
45
+ def address(self):
46
+ if self._address is None:
47
+ self._address = self.interface.get_token_address(self.symbol, self.chain_id)
48
+ return self._address
49
+
50
+ @property
51
+ def decimals(self) -> int:
52
+ if self._decimals is None:
53
+ self._decimals = self.interface.get_token_decimals(
54
+ self.symbol, self.chain_id
55
+ )
56
+ return self._decimals
57
+
58
+ @property
59
+ def info(self):
60
+ if self._info is None:
61
+ self._info = self.interface.get_token_info(self.symbol, self.chain_id)
62
+ return self._info
63
+
64
+ def approve(
65
+ self, spender_address: str, amount: int, owner_address: str, rpc_url: str
66
+ ) -> dict:
67
+ """
68
+ Build an unsigned ERC-20 approve transaction.
69
+ amount is in raw token units — use to_wei() to scale beforehand.
70
+ Pass 2**256 - 1 as amount for an unlimited approval.
71
+ """
72
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
73
+ contract = w3.eth.contract(
74
+ address=Web3.to_checksum_address(self.address),
75
+ abi=_ERC20_APPROVE_ABI,
76
+ )
77
+ owner_cs = Web3.to_checksum_address(owner_address)
78
+ return contract.functions.approve(
79
+ Web3.to_checksum_address(spender_address), amount
80
+ ).build_transaction({"from": owner_cs})
81
+
82
+ def check_allowance(
83
+ self, owner_address: str, spender_address: str, rpc_url: str
84
+ ) -> int:
85
+ """
86
+ Return the current ERC-20 allowance (in raw token units) that
87
+ `spender_address` is approved to spend on behalf of `owner_address`.
88
+ """
89
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
90
+ contract = w3.eth.contract(
91
+ address=Web3.to_checksum_address(self.address),
92
+ abi=_ERC20_ALLOWANCE_ABI,
93
+ )
94
+ return contract.functions.allowance(
95
+ Web3.to_checksum_address(owner_address),
96
+ Web3.to_checksum_address(spender_address),
97
+ ).call()
98
+
99
+
100
+ class TokenInterface:
101
+ def __init__(self):
102
+ self.table_name = "tokens"
103
+
104
+ @property
105
+ def conn(self):
106
+ return _init_tables()
107
+
108
+ def get_token_info(self, symbol: str, chain_id: str) -> pl.DataFrame:
109
+ symbol = symbol.upper()
110
+ chain_id = str(chain_id)
111
+ gen = self._read_token_info(symbol)
112
+ print(f"GEN: {gen}")
113
+ df = self._read_token_info(symbol, chain_id)
114
+ print(f"DF: {df}")
115
+ if not df.is_empty():
116
+ return df
117
+
118
+ # Cache miss — download all chains and persist them.
119
+ downloaded = self._download_token_info(symbol)
120
+ if not downloaded.is_empty():
121
+ self._insert_token_info(downloaded)
122
+
123
+ # Re-read so we only return the requested chain.
124
+ df = self._read_token_info(symbol, chain_id)
125
+ return df
126
+
127
+ def get_token_address(self, symbol: str, chain_id: str):
128
+ df = self.get_token_info(symbol=symbol, chain_id=chain_id)
129
+ return df["address"][0]
130
+
131
+ def get_token_decimals(self, symbol: str, chain_id: str):
132
+ df = self.get_token_info(symbol=symbol, chain_id=chain_id)
133
+ return df["decimals"][0]
134
+
135
+ def batch_save(self, symbols: list):
136
+ if isinstance(symbols, str):
137
+ symbols = [symbols]
138
+
139
+ coin_ids = _find_coin_ids()
140
+
141
+ for s in symbols:
142
+ try:
143
+ df = self._download_token_info(s, coin_ids=coin_ids)
144
+ self._insert_token_info(df)
145
+ except Exception as e:
146
+ break
147
+ except KeyboardInterrupt:
148
+ break
149
+
150
+ def _download_token_info(self, symbol: str, coin_ids: list = None) -> dict:
151
+ if coin_ids is None:
152
+ coin_ids = _find_coin_ids(symbol=symbol)
153
+ else:
154
+ coin_ids = _match_coin_ids(coins=coin_ids, symbol=symbol)
155
+ addresses = _fetch_token_addresses(symbol, coin_ids=coin_ids)
156
+ decimals_map = _fetch_token_decimals(symbol, coin_ids=coin_ids)
157
+ decimals = next(iter(decimals_map.values())) if decimals_map else None
158
+
159
+ data = {
160
+ "chain_id": [],
161
+ "address": [],
162
+ }
163
+ for k, v in addresses.items():
164
+ cid = COINGECKO_CHAIN_IDS.get(k, None)
165
+ data["chain_id"].append(str(cid) if cid is not None else None)
166
+ try:
167
+ address = Web3.to_checksum_address(v)
168
+ except ValueError:
169
+ address = v
170
+ data["address"].append(address)
171
+
172
+ df = pl.DataFrame(data)
173
+ df = df.filter(pl.col("chain_id").is_not_null())
174
+ df = df.with_columns(
175
+ pl.lit(symbol).alias("symbol"), pl.lit(decimals).alias("decimals")
176
+ )
177
+ df = df[["symbol", "chain_id", "address", "decimals"]]
178
+ return df
179
+
180
+ def update_decimals(self, symbol: str, decimals: int) -> None:
181
+ self.conn.execute(
182
+ "UPDATE tokens SET decimals = ? WHERE symbol = ?",
183
+ [decimals, symbol],
184
+ )
185
+
186
+ def _insert_token_info(self, df: pl.DataFrame):
187
+ insert_data(
188
+ df,
189
+ db_cols=["symbol", "chain_id", "address", "decimals"],
190
+ table_name=self.table_name,
191
+ conn=self.conn,
192
+ pk_cols=["chain_id", "address"],
193
+ )
194
+
195
+ def _read_token_info(self, symbol: str, chain_id: str = None) -> pl.DataFrame:
196
+ if chain_id:
197
+ query = f"SELECT * FROM {self.table_name} WHERE symbol = '{symbol}' AND chain_id = '{chain_id}'"
198
+ else:
199
+ query = f"SELECT * FROM {self.table_name} WHERE symbol = '{symbol}'"
200
+ return self.conn.execute(query).pl()
201
+
202
+
203
+ def _find_coin_ids(symbol: str = None) -> list[dict]:
204
+ """Return all CoinGecko coin entries matching the given symbol."""
205
+ coins = requests.get(
206
+ "https://api.coingecko.com/api/v3/coins/list",
207
+ params={"include_platform": "true"},
208
+ timeout=15,
209
+ ).json()
210
+ if symbol:
211
+ return [c for c in coins if c.get("symbol", "").upper() == symbol]
212
+ else:
213
+ return coins
214
+
215
+
216
+ def _match_coin_ids(coins: list, symbol: str):
217
+ return [c for c in coins if c.get("symbol", "").upper() == symbol]
218
+
219
+
220
+ def _fetch_token_addresses(symbol: str, coin_ids: list = None) -> dict[str, str]:
221
+ """
222
+ Given a token symbol (e.g. "WETH"), return {network: address}
223
+ for all networks known to CoinGecko.
224
+ """
225
+ addresses: dict[str, str] = {}
226
+ if coin_ids is None:
227
+ coin_ids = _find_coin_ids(symbol)
228
+ for coin in coin_ids:
229
+ for network, address in coin.get("platforms", {}).items():
230
+ if address:
231
+ addresses[network] = address
232
+ return addresses
233
+
234
+
235
+ def _fetch_token_decimals(symbol: str, coin_ids: list = None) -> dict[str, int]:
236
+ """
237
+ Given a token symbol (e.g. "WETH"), return {network: decimals}
238
+ for every network where CoinGecko reports a decimal_place value.
239
+ """
240
+ if coin_ids is None:
241
+ matches = _find_coin_ids(symbol)
242
+ else:
243
+ matches = coin_ids
244
+ if not matches:
245
+ return {}
246
+
247
+ decimals: dict[str, int] = {}
248
+ for coin in matches:
249
+ detail = requests.get(
250
+ f"https://api.coingecko.com/api/v3/coins/{coin['id']}",
251
+ params={
252
+ "localization": "false",
253
+ "tickers": "false",
254
+ "market_data": "false",
255
+ "community_data": "false",
256
+ "developer_data": "false",
257
+ },
258
+ timeout=15,
259
+ ).json()
260
+ for network, info in detail.get("detail_platforms", {}).items():
261
+ dp = info.get("decimal_place")
262
+ if dp is not None:
263
+ decimals[network] = dp
264
+ return decimals
@@ -0,0 +1,28 @@
1
+ from web3 import Web3
2
+
3
+
4
+ class Wallet:
5
+ def __init__(self, private_key: str, rpc_url: str):
6
+ self.w3 = Web3(Web3.HTTPProvider(rpc_url))
7
+ self.account = self.w3.eth.account.from_key(private_key)
8
+ self.address = self.account.address
9
+
10
+ def sign_and_send(self, tx: dict) -> str:
11
+ """
12
+ Fill in nonce and gas if missing, sign the transaction, broadcast it,
13
+ and return the transaction hash as a hex string.
14
+ """
15
+ if "nonce" not in tx:
16
+ tx["nonce"] = self.w3.eth.get_transaction_count(self.address)
17
+ if "gas" not in tx:
18
+ tx["gas"] = self.w3.eth.estimate_gas({**tx, "from": self.address})
19
+ if "gasPrice" not in tx and "maxFeePerGas" not in tx:
20
+ tx["gasPrice"] = self.w3.eth.gas_price
21
+
22
+ signed = self.account.sign_transaction(tx)
23
+ tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
24
+ return tx_hash.hex()
25
+
26
+ def wait(self, tx_hash: str, timeout: int = 120) -> dict:
27
+ """Block until the transaction is mined and return the receipt."""
28
+ return self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout)
@@ -0,0 +1,354 @@
1
+ Metadata-Version: 2.4
2
+ Name: cryptointerface
3
+ Version: 0.1.0
4
+ Summary: Interface with cryptocurrency and DEX contracts. Store data locally for future use.
5
+ Project-URL: Source, https://github.com/William-Kruta/CryptoInterface
6
+ License: MIT
7
+ Keywords: aave,arbitrage,crypto,defi,dex,uniswap,web3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Office/Business :: Financial
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: duckdb>=1.5
15
+ Requires-Dist: polars>=1.0
16
+ Requires-Dist: pyarrow>=14.0
17
+ Requires-Dist: python-dotenv>=1.0
18
+ Requires-Dist: requests>=2.33
19
+ Requires-Dist: web3>=7.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # CryptoInterface
23
+
24
+ A Python library for interacting with EVM-compatible DEXes — price fetching, swapping.
25
+
26
+ I have provided some starter data to enter in your database.
27
+
28
+ To do this, just write this code:
29
+
30
+ ```
31
+ from cryptointerface.periphery.db import insert_tokens_csv_to_db, insert_pools_csv_to_db
32
+
33
+ insert_tokens_csv_to_db()
34
+ insert_pools_csv_to_db()
35
+ ```
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install cryptointerface
41
+ ```
42
+
43
+ Or with [uv](https://github.com/astral-sh/uv):
44
+
45
+ ```bash
46
+ uv add cryptointerface
47
+ ```
48
+
49
+ **Requirements:** Python 3.12+
50
+
51
+ ---
52
+
53
+ ## Quick Start
54
+
55
+ ```python
56
+ from cryptointerface import Dex, Token, Wallet, arbitrage_route
57
+ from cryptointerface.providers.infura import Infura
58
+ from cryptointerface.periphery.utils import to_wei, from_wei
59
+ from cryptointerface.periphery.enums import Network, Platform
60
+
61
+ infura = Infura(api_key="YOUR_INFURA_KEY")
62
+
63
+ # Look up a token
64
+ token = Token("WETH", chain_id=Network.ARBITRUM.value)
65
+ print(token.address, token.decimals)
66
+
67
+ # Fetch a price
68
+ dex = Dex("uniswap_v3", chain_id="42161", rpc_url=infura.get_url("42161"))
69
+ price = dex.get_price(weth_address, usdc_address)
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Core Modules
75
+
76
+ ### `Token` / `TokenInterface`
77
+
78
+ Resolves token metadata from a local DuckDB cache, downloading from CoinGecko on first use.
79
+
80
+ ```python
81
+ from cryptointerface import Token, TokenInterface
82
+
83
+ token = Token("USDC", chain_id="42161")
84
+ print(token.address) # checksummed address
85
+ print(token.decimals) # 6
86
+ print(token.info) # polars DataFrame
87
+
88
+ # Build an ERC-20 approve transaction (unsigned)
89
+ tx = token.approve(
90
+ spender_address="0xRouter...",
91
+ amount=to_wei(1000, decimals=6),
92
+ owner_address="0xYourWallet",
93
+ rpc_url="https://...",
94
+ )
95
+ ```
96
+
97
+ `TokenInterface` provides the lower-level DB methods (`get_token_info`, `get_token_address`, `get_token_decimals`, `batch_save`, `update_decimals`).
98
+
99
+ ---
100
+
101
+ ### `Dex` / `DexInterface`
102
+
103
+ Fetches pool addresses and prices, and builds swap transactions for all supported DEX protocols.
104
+
105
+ ```python
106
+ from cryptointerface import Dex
107
+
108
+ dex = Dex("uniswap_v3", chain_id="42161", rpc_url="https://...")
109
+
110
+ # Pool address (cached in DuckDB on first call)
111
+ pool = dex.pool_address(token_a_address, token_b_address)
112
+
113
+ # Spot price — token_a denominated in token_b
114
+ price = dex.get_price(token_a_address, token_b_address)
115
+
116
+ # Build an unsigned swap transaction
117
+ tx = dex.interface.swap(
118
+ token_in=token_a_address,
119
+ token_out=token_b_address,
120
+ amount_in=to_wei(1.0),
121
+ dex_name="uniswap_v3",
122
+ chain_id="42161",
123
+ sender="0xYourWallet",
124
+ )
125
+ ```
126
+
127
+ #### Supported DEXes
128
+
129
+ | Name | Protocol | Chains |
130
+ | ---------------- | --------------- | ---------------------------------------------------- |
131
+ | `uniswap_v2` | Uniswap V2 | Ethereum, Sepolia |
132
+ | `uniswap_v3` | Uniswap V3 | Ethereum, Arbitrum, Polygon, Optimism, Base, Sepolia |
133
+ | `sushiswap_v2` | Uniswap V2 fork | Ethereum, Arbitrum, Polygon, Sepolia |
134
+ | `sushiswap_v3` | Uniswap V3 fork | Ethereum, Arbitrum, Polygon, Sepolia |
135
+ | `pancakeswap_v2` | Uniswap V2 fork | BSC, Ethereum |
136
+ | `pancakeswap_v3` | Uniswap V3 fork | BSC, Ethereum |
137
+ | `quickswap_v2` | Uniswap V2 fork | Polygon |
138
+ | `quickswap_v3` | Algebra V3 | Polygon |
139
+ | `camelot_v2` | Uniswap V2 fork | Arbitrum |
140
+ | `camelot_v3` | Algebra V3 | Arbitrum |
141
+ | `traderjoe_v1` | Uniswap V2 fork | Avalanche, Arbitrum |
142
+ | `aerodrome` | Solidly V2 | Base |
143
+ | `velodrome` | Solidly V2 | Optimism |
144
+
145
+ #### Protocols
146
+
147
+ - **Uniswap V2** — `getPair` factory, fixed fee per DEX, `getReserves` pricing
148
+ - **Uniswap V3** — `getPool` factory, per-pool fee tiers, `sqrtPriceX96` pricing
149
+ - **Algebra V3** — `poolByPair` factory, dynamic fees, no fee argument (QuickSwap, Camelot V3)
150
+ - **Solidly V2** — `getPool(stable: bool)` factory, stable/volatile pools (Aerodrome, Velodrome)
151
+
152
+ #### `create_dex_mapping`
153
+
154
+ Build a multi-DEX dict for use with `get_prices` / `arbitrage_route`:
155
+
156
+ ```python
157
+ from cryptointerface.dex import create_dex_mapping
158
+
159
+ dex_mapping = create_dex_mapping(
160
+ dex_names=["uniswap_v3", "sushiswap_v3", "camelot_v3"],
161
+ chain_ids=["42161"],
162
+ infura_obj=infura,
163
+ )
164
+ ```
165
+
166
+ ---
167
+
168
+ ### `routes` — Prices & Arbitrage
169
+
170
+ ```python
171
+ from cryptointerface import get_prices, arbitrage_route
172
+
173
+ # Parallel price fetch across all DEXes
174
+ prices = get_prices(dex_mapping, token_a_address, token_b_address)
175
+ # {"uniswap_v3": {"price": 1823.4, "pool": "0x...", "fee_bps": 5}, ...}
176
+
177
+ # Arbitrage analysis
178
+ route = arbitrage_route(dex_mapping, token_a_address, token_b_address)
179
+ ```
180
+
181
+ `arbitrage_route` returns:
182
+
183
+ ```python
184
+ {
185
+ "buy": {"dex": "camelot_v3", "price": 1820.1, "pool": "0x...", "fee_bps": 5},
186
+ "sell": {"dex": "uniswap_v3", "price": 1825.8, "pool": "0x...", "fee_bps": 5},
187
+ "spread_abs": 5.7,
188
+ "spread_pct": 0.3132,
189
+ "total_fee_pct": 0.1, # both legs combined
190
+ "net_spread_pct": 0.2132, # spread after fees
191
+ "profitable": True,
192
+ "all_prices": {...},
193
+ }
194
+ ```
195
+
196
+ Fees are sourced from `dex_architecture.json` — V3 fee tiers use the matched pool tier; V2 uses the fixed DEX fee.
197
+
198
+ ---
199
+
200
+ ### `Wallet`
201
+
202
+ Signs and broadcasts unsigned transactions built by `Dex`, `Token`, or `AaveFlashLoan`.
203
+
204
+ ```python
205
+ from cryptointerface import Wallet
206
+
207
+ wallet = Wallet(private_key="0x...", rpc_url="https://...")
208
+
209
+ tx_hash = wallet.sign_and_send(tx) # auto-fills nonce, gas, gasPrice
210
+ receipt = wallet.wait(tx_hash) # blocks until mined
211
+ ```
212
+
213
+ ---
214
+
215
+ ### `AaveFlashLoan`
216
+
217
+ Builds Aave V3 flash loan transactions. Supported chains: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Sepolia.
218
+
219
+ Flash loan fee: **0.09%** (9 bps).
220
+
221
+ ```python
222
+ from cryptointerface import AaveFlashLoan
223
+ from cryptointerface.periphery.utils import to_wei
224
+
225
+ fl = AaveFlashLoan(chain_id="42161", rpc_url="https://...")
226
+
227
+ # Single-asset flash loan (lower gas)
228
+ tx = fl.flash_loan_simple_tx(
229
+ receiver_contract="0xYourDeployedExecutor",
230
+ asset=usdc_address,
231
+ amount=to_wei(10_000, decimals=6),
232
+ sender=wallet.address,
233
+ )
234
+ tx_hash = wallet.sign_and_send(tx)
235
+
236
+ # Multi-asset flash loan
237
+ tx = fl.flash_loan_tx(
238
+ receiver_contract="0xYourDeployedExecutor",
239
+ assets=[weth_address, usdc_address],
240
+ amounts=[to_wei(5.0), to_wei(10_000, decimals=6)],
241
+ sender=wallet.address,
242
+ )
243
+
244
+ # Check profitability after the flash loan fee
245
+ result = AaveFlashLoan.net_profit(
246
+ amount=to_wei(10_000, decimals=6),
247
+ gross_profit_wei=to_wei(15, decimals=6),
248
+ token_decimals=6,
249
+ )
250
+ # {"fee_wei": 9000, "net_profit_wei": 6000, "net_profit_human": 0.006, "is_profitable": True}
251
+ ```
252
+
253
+ #### Solidity Executor Contract
254
+
255
+ The flash loan callback (`executeOperation`) must be implemented in Solidity. A full template is available as:
256
+
257
+ ```python
258
+ print(AaveFlashLoan.EXECUTOR_TEMPLATE)
259
+ ```
260
+
261
+ Deploy this contract once, paste your swap logic inside `executeOperation`, then pass its address as `receiver_contract`.
262
+
263
+ ---
264
+
265
+ ### `Infura`
266
+
267
+ RPC URL builder for Infura endpoints.
268
+
269
+ ```python
270
+ from cryptointerface.providers.infura import Infura
271
+
272
+ infura = Infura(api_key="YOUR_KEY")
273
+ rpc_url = infura.get_url("42161") # Arbitrum
274
+
275
+ # Add a custom endpoint
276
+ infura.add_endpoint(chain_id="10", endpoint="https://optimism-mainnet.infura.io/v3/")
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Local Database (DuckDB)
282
+
283
+ Token and pool data is cached locally in a DuckDB file (`~/.cryptointerface/data.db` by default). All reads/writes are thread-safe via thread-local connections and a write lock.
284
+
285
+ ### Schema
286
+
287
+ **`tokens`** — `symbol`, `chain_id`, `address`, `decimals`
288
+
289
+ **`pools_v2`** — `dex`, `chain_id`, `token_0`, `token_1`, `address`
290
+
291
+ **`pools_v3`** — `dex`, `chain_id`, `token_0`, `token_1`, `fee`, `address`
292
+
293
+ ### CSV Import / Export
294
+
295
+ ```python
296
+ from cryptointerface.periphery.db import (
297
+ export_tokens_to_csv,
298
+ export_pools_to_csv,
299
+ insert_tokens_csv_to_db,
300
+ insert_pools_csv_to_db,
301
+ )
302
+
303
+ export_tokens_to_csv("tokens.csv")
304
+ export_pools_to_csv("pools_v2.csv", "pools_v3.csv")
305
+
306
+ insert_tokens_csv_to_db("tokens.csv")
307
+ insert_pools_csv_to_db("pools_v2.csv", "pools_v3.csv")
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Utilities
313
+
314
+ ```python
315
+ from cryptointerface.periphery.utils import to_wei, from_wei
316
+
317
+ to_wei(1.5, decimals=18) # 1500000000000000000
318
+ from_wei(1_000_000, decimals=6) # 1.0
319
+ ```
320
+
321
+ ### Enums
322
+
323
+ ```python
324
+ from cryptointerface.periphery.enums import Network, Platform
325
+
326
+ Network.ARBITRUM.value # "42161"
327
+ Platform.UNISWAP_V3.value # "uniswap_v3"
328
+ ```
329
+
330
+ **Networks:** `ETHEREUM`, `OPTIMISM`, `BSC`, `POLYGON`, `BASE`, `ARBITRUM`, `CELO`, `AVALANCHE`, `BLAST`, `ZORA`, `SEPOLIA`
331
+
332
+ **Platforms:** `UNISWAP_V2`, `UNISWAP_V3`, `SUSHISWAP_V2`, `SUSHISWAP_V3`, `PANCAKESWAP_V2`, `PANCAKESWAP_V3`, `QUICKSWAP_V2`, `QUICKSWAP_V3`, `CAMELOT_V2`, `CAMELOT_V3`, `TRADERJOE_V1`, `AERODROME`, `VELODROME`
333
+
334
+ ---
335
+
336
+ ## Running Tests
337
+
338
+ ```bash
339
+ uv run pytest
340
+ ```
341
+
342
+ Network tests (require a live RPC) are skipped by default unless `SEPOLIA_RPC_URL` is set in your environment.
343
+
344
+ ---
345
+
346
+ ## Dependencies
347
+
348
+ | Package | Purpose |
349
+ | --------------- | ---------------------------- |
350
+ | `web3` | EVM RPC interaction |
351
+ | `duckdb` | Local persistent storage |
352
+ | `polars` | DataFrame operations |
353
+ | `requests` | CoinGecko API calls |
354
+ | `python-dotenv` | Environment variable loading |
@@ -0,0 +1,19 @@
1
+ cryptointerface/__init__.py,sha256=rCrUO5e1Jt-TnAKz49Tdvi0vTxGoTBFO8i7hG4gn6DM,208
2
+ cryptointerface/dex.py,sha256=XZjUrG_rSD42gR6HPh2L8zauXFBg9YoxMxYyhs7c2Lg,30980
3
+ cryptointerface/flashloan.py,sha256=hKwbI45GgVX2e6zrDiNOEQI70MtnWjq0Tnk6jIkXgwg,11673
4
+ cryptointerface/routes.py,sha256=cuwv1z5MuOQ1L_-ck6gBoLT_bnRZNpduP4rLqUMhJSo,3643
5
+ cryptointerface/token.py,sha256=SszyrMdM_USYHrkP6lmi96_3hwmLfmzZikoDMV9xms4,8808
6
+ cryptointerface/wallet.py,sha256=HdSOt-6-LabM8R7cpj-cDm9kl3l6uZ5QJcozLI0RpqA,1160
7
+ cryptointerface/periphery/config.py,sha256=Pyu54RZSiafOefQ-h-5r0ubkWq3J_plTeE3iyYgFOik,1248
8
+ cryptointerface/periphery/db.py,sha256=NlqywCHZK-JqFCb4OiwxK9JLQCY5ZCoCBpjbq1JuY94,4307
9
+ cryptointerface/periphery/dex_architecture.json,sha256=nShSIgX2ZOhRQi1sV0GzBLW3SNsZ4UihamrQafQIX1g,3838
10
+ cryptointerface/periphery/dex_contracts.json,sha256=nN1dXSY53d0YBfREggbJRPxGkWOK-CJDMOCvRziHneY,13288
11
+ cryptointerface/periphery/dex_contracts.py,sha256=QROcJYi9Jfr9zqTaF3L06bszCPUAacQDIJKvS5aIlNU,1541
12
+ cryptointerface/periphery/enums.py,sha256=1uWJb9xYPDTUnTap4UukHWK4XNwVQszWfebIH2edCt0,783
13
+ cryptointerface/periphery/mapping.py,sha256=RnX7kH76IcsVJhLS54pIyeeID69QZQRNnmCKHgqssN4,2780
14
+ cryptointerface/periphery/utils.py,sha256=ag9tMjJ7ugTL7Cs4PgyYxr5DoiiH31cLEt4NNcEzPak,203
15
+ cryptointerface/providers/endpoints.json,sha256=MJi4KJu73_0As3Nza-vF0XCUr5D_SsQ0zy22T6Vig9Q,1186
16
+ cryptointerface/providers/infura.py,sha256=3o4Ru4FLJXcHvWbrx5fgOVnWJjXElSl5cQvK4p6fcMs,1011
17
+ cryptointerface-0.1.0.dist-info/METADATA,sha256=s6XOY2KzDcFpWnp6PoZyNbtVEylN7PgIK5PU9L0Sln0,10506
18
+ cryptointerface-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
19
+ cryptointerface-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any