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.
Files changed (80) hide show
  1. uni_exec_engine-0.2.0.dist-info/METADATA +576 -0
  2. uni_exec_engine-0.2.0.dist-info/RECORD +80 -0
  3. uni_exec_engine-0.2.0.dist-info/WHEEL +5 -0
  4. uni_exec_engine-0.2.0.dist-info/licenses/LICENSE +21 -0
  5. uni_exec_engine-0.2.0.dist-info/top_level.txt +1 -0
  6. uniswap_autopilot/__init__.py +3 -0
  7. uniswap_autopilot/analytics/__init__.py +0 -0
  8. uniswap_autopilot/analytics/il_calculator.py +525 -0
  9. uniswap_autopilot/analytics/portfolio.py +234 -0
  10. uniswap_autopilot/analytics/position.py +433 -0
  11. uniswap_autopilot/analytics/range_suggest.py +270 -0
  12. uniswap_autopilot/audit.py +194 -0
  13. uniswap_autopilot/common/__init__.py +0 -0
  14. uniswap_autopilot/common/approval_cleanup.py +255 -0
  15. uniswap_autopilot/common/check_balance.py +64 -0
  16. uniswap_autopilot/common/common.py +804 -0
  17. uniswap_autopilot/common/deep_link.py +132 -0
  18. uniswap_autopilot/common/gas.py +141 -0
  19. uniswap_autopilot/data/auto_trade_policy.example.json +29 -0
  20. uniswap_autopilot/data/chains.json +135 -0
  21. uniswap_autopilot/data/common-token-addresses.json +777 -0
  22. uniswap_autopilot/execute/__init__.py +0 -0
  23. uniswap_autopilot/execute/_internal/__init__.py +5 -0
  24. uniswap_autopilot/execute/_internal/constants.py +15 -0
  25. uniswap_autopilot/execute/_internal/preflight.py +150 -0
  26. uniswap_autopilot/execute/_internal/pure_signer.py +182 -0
  27. uniswap_autopilot/execute/_internal/rpc.py +462 -0
  28. uniswap_autopilot/execute/_internal/signer.py +298 -0
  29. uniswap_autopilot/execute/_internal/submit.py +73 -0
  30. uniswap_autopilot/execute/_internal/tx.py +370 -0
  31. uniswap_autopilot/execute/broadcast.py +380 -0
  32. uniswap_autopilot/execute/detect.py +52 -0
  33. uniswap_autopilot/execute/telegram_confirm.py +272 -0
  34. uniswap_autopilot/lp/compare_pools.py +338 -0
  35. uniswap_autopilot/lp/v2/__init__.py +0 -0
  36. uniswap_autopilot/lp/v2/approve.py +100 -0
  37. uniswap_autopilot/lp/v2/build_tx.py +226 -0
  38. uniswap_autopilot/lp/v2/flow.py +204 -0
  39. uniswap_autopilot/lp/v2/pair.py +135 -0
  40. uniswap_autopilot/lp/v2/positions.py +177 -0
  41. uniswap_autopilot/lp/v3/__init__.py +0 -0
  42. uniswap_autopilot/lp/v3/approve.py +109 -0
  43. uniswap_autopilot/lp/v3/auto_rebalance.py +282 -0
  44. uniswap_autopilot/lp/v3/build_tx.py +465 -0
  45. uniswap_autopilot/lp/v3/compound.py +258 -0
  46. uniswap_autopilot/lp/v3/flow.py +358 -0
  47. uniswap_autopilot/lp/v3/pool.py +175 -0
  48. uniswap_autopilot/lp/v3/position.py +112 -0
  49. uniswap_autopilot/lp/v3/tick.py +75 -0
  50. uniswap_autopilot/lp/v4/__init__.py +0 -0
  51. uniswap_autopilot/lp/v4/approve.py +105 -0
  52. uniswap_autopilot/lp/v4/build_tx.py +669 -0
  53. uniswap_autopilot/lp/v4/flow.py +368 -0
  54. uniswap_autopilot/lp/v4/pool.py +174 -0
  55. uniswap_autopilot/lp/v4/position.py +185 -0
  56. uniswap_autopilot/policy.py +371 -0
  57. uniswap_autopilot/price_feed.py +100 -0
  58. uniswap_autopilot/py.typed +0 -0
  59. uniswap_autopilot/search/__init__.py +0 -0
  60. uniswap_autopilot/search/risk.py +200 -0
  61. uniswap_autopilot/search/search.py +580 -0
  62. uniswap_autopilot/state_machine.py +315 -0
  63. uniswap_autopilot/swap/__init__.py +1 -0
  64. uniswap_autopilot/swap/deep_link.py +69 -0
  65. uniswap_autopilot/swap/extensions/__init__.py +2 -0
  66. uniswap_autopilot/swap/extensions/bridge.py +197 -0
  67. uniswap_autopilot/swap/extensions/limit_order.py +272 -0
  68. uniswap_autopilot/swap/extensions/slippage.py +123 -0
  69. uniswap_autopilot/swap/flow.py +693 -0
  70. uniswap_autopilot/swap/flow_core/__init__.py +2 -0
  71. uniswap_autopilot/swap/flow_core/artifacts.py +11 -0
  72. uniswap_autopilot/swap/flow_core/broadcast.py +50 -0
  73. uniswap_autopilot/swap/flow_core/diagnostics.py +216 -0
  74. uniswap_autopilot/swap/flow_core/paper.py +113 -0
  75. uniswap_autopilot/swap/flow_core/policy.py +142 -0
  76. uniswap_autopilot/swap/links/__init__.py +2 -0
  77. uniswap_autopilot/swap/links/deep_link.py +69 -0
  78. uniswap_autopilot/swap/trading_api/permit.py +41 -0
  79. uniswap_autopilot/swap/trading_api/quote.py +282 -0
  80. 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()