uni-exec-engine 0.2.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.
- uni_exec_engine-0.2.0.dist-info/METADATA +576 -0
- uni_exec_engine-0.2.0.dist-info/RECORD +80 -0
- uni_exec_engine-0.2.0.dist-info/WHEEL +5 -0
- uni_exec_engine-0.2.0.dist-info/licenses/LICENSE +21 -0
- uni_exec_engine-0.2.0.dist-info/top_level.txt +1 -0
- uniswap_autopilot/__init__.py +3 -0
- uniswap_autopilot/analytics/__init__.py +0 -0
- uniswap_autopilot/analytics/il_calculator.py +525 -0
- uniswap_autopilot/analytics/portfolio.py +234 -0
- uniswap_autopilot/analytics/position.py +433 -0
- uniswap_autopilot/analytics/range_suggest.py +270 -0
- uniswap_autopilot/audit.py +194 -0
- uniswap_autopilot/common/__init__.py +0 -0
- uniswap_autopilot/common/approval_cleanup.py +255 -0
- uniswap_autopilot/common/check_balance.py +64 -0
- uniswap_autopilot/common/common.py +804 -0
- uniswap_autopilot/common/deep_link.py +132 -0
- uniswap_autopilot/common/gas.py +141 -0
- uniswap_autopilot/data/auto_trade_policy.example.json +29 -0
- uniswap_autopilot/data/chains.json +135 -0
- uniswap_autopilot/data/common-token-addresses.json +777 -0
- uniswap_autopilot/execute/__init__.py +0 -0
- uniswap_autopilot/execute/_internal/__init__.py +5 -0
- uniswap_autopilot/execute/_internal/constants.py +15 -0
- uniswap_autopilot/execute/_internal/preflight.py +150 -0
- uniswap_autopilot/execute/_internal/pure_signer.py +182 -0
- uniswap_autopilot/execute/_internal/rpc.py +462 -0
- uniswap_autopilot/execute/_internal/signer.py +298 -0
- uniswap_autopilot/execute/_internal/submit.py +73 -0
- uniswap_autopilot/execute/_internal/tx.py +370 -0
- uniswap_autopilot/execute/broadcast.py +380 -0
- uniswap_autopilot/execute/detect.py +52 -0
- uniswap_autopilot/execute/telegram_confirm.py +272 -0
- uniswap_autopilot/lp/compare_pools.py +338 -0
- uniswap_autopilot/lp/v2/__init__.py +0 -0
- uniswap_autopilot/lp/v2/approve.py +100 -0
- uniswap_autopilot/lp/v2/build_tx.py +226 -0
- uniswap_autopilot/lp/v2/flow.py +204 -0
- uniswap_autopilot/lp/v2/pair.py +135 -0
- uniswap_autopilot/lp/v2/positions.py +177 -0
- uniswap_autopilot/lp/v3/__init__.py +0 -0
- uniswap_autopilot/lp/v3/approve.py +109 -0
- uniswap_autopilot/lp/v3/auto_rebalance.py +282 -0
- uniswap_autopilot/lp/v3/build_tx.py +465 -0
- uniswap_autopilot/lp/v3/compound.py +258 -0
- uniswap_autopilot/lp/v3/flow.py +358 -0
- uniswap_autopilot/lp/v3/pool.py +175 -0
- uniswap_autopilot/lp/v3/position.py +112 -0
- uniswap_autopilot/lp/v3/tick.py +75 -0
- uniswap_autopilot/lp/v4/__init__.py +0 -0
- uniswap_autopilot/lp/v4/approve.py +105 -0
- uniswap_autopilot/lp/v4/build_tx.py +669 -0
- uniswap_autopilot/lp/v4/flow.py +368 -0
- uniswap_autopilot/lp/v4/pool.py +174 -0
- uniswap_autopilot/lp/v4/position.py +185 -0
- uniswap_autopilot/policy.py +371 -0
- uniswap_autopilot/price_feed.py +100 -0
- uniswap_autopilot/py.typed +0 -0
- uniswap_autopilot/search/__init__.py +0 -0
- uniswap_autopilot/search/risk.py +200 -0
- uniswap_autopilot/search/search.py +580 -0
- uniswap_autopilot/state_machine.py +315 -0
- uniswap_autopilot/swap/__init__.py +1 -0
- uniswap_autopilot/swap/deep_link.py +69 -0
- uniswap_autopilot/swap/extensions/__init__.py +2 -0
- uniswap_autopilot/swap/extensions/bridge.py +197 -0
- uniswap_autopilot/swap/extensions/limit_order.py +272 -0
- uniswap_autopilot/swap/extensions/slippage.py +123 -0
- uniswap_autopilot/swap/flow.py +693 -0
- uniswap_autopilot/swap/flow_core/__init__.py +2 -0
- uniswap_autopilot/swap/flow_core/artifacts.py +11 -0
- uniswap_autopilot/swap/flow_core/broadcast.py +50 -0
- uniswap_autopilot/swap/flow_core/diagnostics.py +216 -0
- uniswap_autopilot/swap/flow_core/paper.py +113 -0
- uniswap_autopilot/swap/flow_core/policy.py +142 -0
- uniswap_autopilot/swap/links/__init__.py +2 -0
- uniswap_autopilot/swap/links/deep_link.py +69 -0
- uniswap_autopilot/swap/trading_api/permit.py +41 -0
- uniswap_autopilot/swap/trading_api/quote.py +282 -0
- uniswap_autopilot/swap/trading_api/swap.py +248 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from uniswap_autopilot.common.common import dump_json, normalize_chain, resolve_token, load_local_env
|
|
11
|
+
from uniswap_autopilot.search.search import _ds_lookup
|
|
12
|
+
|
|
13
|
+
# Price feed integration
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _age_days(pair_created_at_ms: Any) -> float | None:
|
|
17
|
+
"""Convert a DexScreener ``pairCreatedAt`` (ms epoch) to age in days."""
|
|
18
|
+
if pair_created_at_ms in (None, 0, "0"):
|
|
19
|
+
return None
|
|
20
|
+
try:
|
|
21
|
+
created_s = float(pair_created_at_ms) / 1000.0
|
|
22
|
+
except (ValueError, TypeError):
|
|
23
|
+
return None
|
|
24
|
+
import time
|
|
25
|
+
|
|
26
|
+
age = (time.time() - created_s) / 86_400.0
|
|
27
|
+
return age if age >= 0 else None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _compute_risk(
|
|
31
|
+
liquidity_usd: float,
|
|
32
|
+
volume_24h: float,
|
|
33
|
+
market_cap: float,
|
|
34
|
+
price_change_24h: float,
|
|
35
|
+
pair_age_days: float | None = None,
|
|
36
|
+
) -> tuple[int, list[str]]:
|
|
37
|
+
score = 0
|
|
38
|
+
warnings: list[str] = []
|
|
39
|
+
|
|
40
|
+
# Liquidity
|
|
41
|
+
if liquidity_usd < 10_000:
|
|
42
|
+
score += 30
|
|
43
|
+
warnings.append(f"Very low liquidity: ${liquidity_usd:,.0f}")
|
|
44
|
+
elif liquidity_usd < 100_000:
|
|
45
|
+
score += 15
|
|
46
|
+
warnings.append(f"Low liquidity: ${liquidity_usd:,.0f}")
|
|
47
|
+
elif liquidity_usd < 1_000_000:
|
|
48
|
+
score += 5
|
|
49
|
+
warnings.append(f"Moderate liquidity: ${liquidity_usd:,.0f}")
|
|
50
|
+
|
|
51
|
+
# Volume
|
|
52
|
+
if volume_24h < 1_000:
|
|
53
|
+
score += 20
|
|
54
|
+
warnings.append(f"Very low 24h volume: ${volume_24h:,.0f}")
|
|
55
|
+
elif volume_24h < 10_000:
|
|
56
|
+
score += 10
|
|
57
|
+
warnings.append(f"Low 24h volume: ${volume_24h:,.0f}")
|
|
58
|
+
|
|
59
|
+
# Market cap
|
|
60
|
+
if market_cap < 50_000:
|
|
61
|
+
score += 15
|
|
62
|
+
warnings.append(f"Very low market cap: ${market_cap:,.0f}")
|
|
63
|
+
elif market_cap < 500_000:
|
|
64
|
+
score += 8
|
|
65
|
+
warnings.append(f"Low market cap: ${market_cap:,.0f}")
|
|
66
|
+
|
|
67
|
+
# Price change
|
|
68
|
+
abs_change = abs(price_change_24h)
|
|
69
|
+
if abs_change > 50:
|
|
70
|
+
score += 15
|
|
71
|
+
warnings.append(f"Extreme price change: {price_change_24h:+.1f}%")
|
|
72
|
+
elif abs_change > 30:
|
|
73
|
+
score += 8
|
|
74
|
+
warnings.append(f"Large price change: {price_change_24h:+.1f}%")
|
|
75
|
+
|
|
76
|
+
# Pair age — freshly-created pairs are a major rug-pull signal.
|
|
77
|
+
if pair_age_days is not None:
|
|
78
|
+
if pair_age_days < 1:
|
|
79
|
+
score += 25
|
|
80
|
+
warnings.append(f"Pair created <24h ago ({pair_age_days * 24:.0f}h)")
|
|
81
|
+
elif pair_age_days < 7:
|
|
82
|
+
score += 15
|
|
83
|
+
warnings.append(f"Very new pair ({pair_age_days:.1f} days old)")
|
|
84
|
+
elif pair_age_days < 30:
|
|
85
|
+
score += 5
|
|
86
|
+
warnings.append(f"New pair ({pair_age_days:.0f} days old)")
|
|
87
|
+
|
|
88
|
+
return score, warnings
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _risk_level(score: int) -> str:
|
|
92
|
+
if score <= 25:
|
|
93
|
+
return "LOW"
|
|
94
|
+
if score <= 50:
|
|
95
|
+
return "MEDIUM"
|
|
96
|
+
if score <= 75:
|
|
97
|
+
return "HIGH"
|
|
98
|
+
return "EXTREME"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def assess_risk(chain_name: str, token_name: str) -> dict[str, Any]:
|
|
102
|
+
chain = normalize_chain(chain_name)
|
|
103
|
+
token = resolve_token(chain, token_name)
|
|
104
|
+
|
|
105
|
+
address = token["address"]
|
|
106
|
+
if address == "NATIVE":
|
|
107
|
+
return {
|
|
108
|
+
"action": "risk_assessment",
|
|
109
|
+
"chain": {"key": chain.key, "chainId": chain.chain_id},
|
|
110
|
+
"token": token,
|
|
111
|
+
"riskScore": 0,
|
|
112
|
+
"riskLevel": "LOW",
|
|
113
|
+
"warnings": ["Native token - risk scoring does not apply"],
|
|
114
|
+
"marketData": None,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Fetch DexScreener data
|
|
118
|
+
ds_data = _ds_lookup(chain.key, address)
|
|
119
|
+
liquidity_usd = 0.0
|
|
120
|
+
volume_24h = 0.0
|
|
121
|
+
market_cap = 0.0
|
|
122
|
+
price_change_24h = 0.0
|
|
123
|
+
price_usd: float | None = None
|
|
124
|
+
pair_age_days: float | None = None
|
|
125
|
+
|
|
126
|
+
if ds_data:
|
|
127
|
+
liquidity_usd = float(ds_data.get("liquidityUsd") or 0)
|
|
128
|
+
volume_24h = float(ds_data.get("volume24h") or 0)
|
|
129
|
+
market_cap = float(ds_data.get("marketCap") or 0)
|
|
130
|
+
price_changes = ds_data.get("priceChange") or {}
|
|
131
|
+
price_change_24h = float(price_changes.get("h24") or 0)
|
|
132
|
+
price_usd = ds_data.get("priceUsd")
|
|
133
|
+
pair_age_days = _age_days(ds_data.get("pairCreatedAt"))
|
|
134
|
+
if price_usd is not None:
|
|
135
|
+
try:
|
|
136
|
+
price_usd = float(price_usd)
|
|
137
|
+
except (ValueError, TypeError):
|
|
138
|
+
price_usd = None
|
|
139
|
+
|
|
140
|
+
# Fetch price from price-feed (multi-source fallback)
|
|
141
|
+
if price_usd is None:
|
|
142
|
+
from price_feed import get_price
|
|
143
|
+
result = get_price(chain.key, address, tier="normal")
|
|
144
|
+
if result:
|
|
145
|
+
price_usd = result["price"]
|
|
146
|
+
|
|
147
|
+
score, warnings = _compute_risk(
|
|
148
|
+
liquidity_usd, volume_24h, market_cap, price_change_24h, pair_age_days
|
|
149
|
+
)
|
|
150
|
+
level = _risk_level(score)
|
|
151
|
+
|
|
152
|
+
market_data: dict[str, Any] = {
|
|
153
|
+
"liquidityUsd": liquidity_usd,
|
|
154
|
+
"volume24h": volume_24h,
|
|
155
|
+
"marketCap": market_cap,
|
|
156
|
+
"priceChange24h": price_change_24h,
|
|
157
|
+
}
|
|
158
|
+
if pair_age_days is not None:
|
|
159
|
+
market_data["pairAgeDays"] = round(pair_age_days, 2)
|
|
160
|
+
if price_usd is not None:
|
|
161
|
+
market_data["priceUsd"] = price_usd
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
"action": "risk_assessment",
|
|
165
|
+
"chain": {"key": chain.key, "chainId": chain.chain_id},
|
|
166
|
+
"token": token,
|
|
167
|
+
"riskScore": score,
|
|
168
|
+
"riskLevel": level,
|
|
169
|
+
"warnings": warnings,
|
|
170
|
+
"marketData": market_data,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main() -> None:
|
|
175
|
+
parser = argparse.ArgumentParser(description="Token risk assessment")
|
|
176
|
+
parser.add_argument("--chain", required=True, help="Chain name, e.g. base / ethereum")
|
|
177
|
+
parser.add_argument("--token", required=True, help="Token symbol, address, or NATIVE")
|
|
178
|
+
|
|
179
|
+
args = parser.parse_args()
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
load_local_env()
|
|
183
|
+
result = assess_risk(args.chain, args.token)
|
|
184
|
+
|
|
185
|
+
print(f"Risk Score: {result['riskScore']}/100")
|
|
186
|
+
print(f"Risk Level: {result['riskLevel']}")
|
|
187
|
+
if result["warnings"]:
|
|
188
|
+
print("Warnings:")
|
|
189
|
+
for w in result["warnings"]:
|
|
190
|
+
print(f" - {w}")
|
|
191
|
+
|
|
192
|
+
dump_json(result)
|
|
193
|
+
|
|
194
|
+
except Exception as exc:
|
|
195
|
+
print(json.dumps({"error": str(exc)}, ensure_ascii=False, indent=2), file=sys.stderr)
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
if __name__ == "__main__":
|
|
200
|
+
main()
|