primecli 0.5.7__tar.gz → 0.5.9__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.
- {primecli-0.5.7 → primecli-0.5.9}/PKG-INFO +1 -1
- {primecli-0.5.7 → primecli-0.5.9}/primecli/degenprime.py +10 -1
- {primecli-0.5.7 → primecli-0.5.9}/primecli/health_monitor.py +97 -76
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/PKG-INFO +1 -1
- {primecli-0.5.7 → primecli-0.5.9}/pyproject.toml +1 -1
- {primecli-0.5.7 → primecli-0.5.9}/LICENSE +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/README.md +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli/__init__.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli/arbprime.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli/deltaprime.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/SOURCES.txt +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/dependency_links.txt +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/entry_points.txt +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/requires.txt +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/primecli.egg-info/top_level.txt +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/setup.cfg +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_cross_file_identity.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_gas_pricing.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_health_monitor.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_paraswap_validator.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_redstone_encoding.py +0 -0
- {primecli-0.5.7 → primecli-0.5.9}/tests/test_to_wei_units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.9
|
|
4
4
|
Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
|
|
5
5
|
Author: Mnemosyne-quest contributors
|
|
6
6
|
License: MIT
|
|
@@ -310,6 +310,8 @@ def resolve_private_key():
|
|
|
310
310
|
Raises with a clear message if none resolve."""
|
|
311
311
|
if _CLI_KEY:
|
|
312
312
|
return _CLI_KEY.strip()
|
|
313
|
+
if _SELECTED_AGENT:
|
|
314
|
+
return _agent_key(_SELECTED_AGENT)
|
|
313
315
|
for env_var in ("DEGENPRIME_PRIVATE_KEY", "DELTAPRIME_PRIVATE_KEY"):
|
|
314
316
|
raw = os.environ.get(env_var)
|
|
315
317
|
if raw:
|
|
@@ -2548,7 +2550,7 @@ def main():
|
|
|
2548
2550
|
def _dispatch():
|
|
2549
2551
|
args = sys.argv[1:] if len(sys.argv) > 1 else []
|
|
2550
2552
|
# Global signing-key override: --key <0xhex>, stripped before command dispatch.
|
|
2551
|
-
global _CLI_KEY
|
|
2553
|
+
global _SELECTED_AGENT, _CLI_KEY
|
|
2552
2554
|
if "--key" in args:
|
|
2553
2555
|
i = args.index("--key")
|
|
2554
2556
|
if i + 1 >= len(args):
|
|
@@ -2556,6 +2558,13 @@ def _dispatch():
|
|
|
2556
2558
|
return
|
|
2557
2559
|
_CLI_KEY = args[i + 1]
|
|
2558
2560
|
del args[i:i + 2]
|
|
2561
|
+
if "--as" in args:
|
|
2562
|
+
i = args.index("--as")
|
|
2563
|
+
if i + 1 >= len(args):
|
|
2564
|
+
print("--as requires an agent name. Example: --as parakletos")
|
|
2565
|
+
return
|
|
2566
|
+
_SELECTED_AGENT = args[i + 1]
|
|
2567
|
+
del args[i:i + 2]
|
|
2559
2568
|
if not args or args[0] in ("-h", "--help"):
|
|
2560
2569
|
print(__doc__)
|
|
2561
2570
|
return
|
|
@@ -22,6 +22,7 @@ Strategy config (JSON):
|
|
|
22
22
|
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
|
+
import re
|
|
25
26
|
import subprocess
|
|
26
27
|
import sys
|
|
27
28
|
import time
|
|
@@ -36,18 +37,22 @@ TIER_MAX = {"basic": 5, "premium": 10}
|
|
|
36
37
|
# Health computation
|
|
37
38
|
# ════════════════════════════════════════════════════════════════════
|
|
38
39
|
|
|
39
|
-
def compute_health(
|
|
40
|
-
|
|
40
|
+
def compute_health(
|
|
41
|
+
defi_data: dict,
|
|
42
|
+
max_mult: int = 10,
|
|
43
|
+
per_asset_powers: dict[str, int] | None = None,
|
|
44
|
+
) -> dict:
|
|
45
|
+
"""Compute health (0-100%) using the cross-margin formula from DeltaPrime docs.
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
Cross-margin formula (when per_asset_powers is provided):
|
|
48
|
+
Pr_i = power_i / (power_i + 1) # borrowing power ratio per asset
|
|
49
|
+
Cw_i = supplied_usd_i x Pr_i # weighted collateral per asset
|
|
50
|
+
Bw_i = borrowed_usd_i x Pr_i # weighted borrows per asset
|
|
51
|
+
H = (SigmaCw + SigmaBw - B) / SigmaCw x 100
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
which simplifies to: 100 * (1 - debt / (max_mult * equity)).
|
|
53
|
+
Falls back to the simplified uniform formula when per_asset_powers is None
|
|
54
|
+
(all assets assumed at max_mult borrowing power):
|
|
55
|
+
health_pct = 100 * (1 - debt / (max_mult * equity))
|
|
51
56
|
|
|
52
57
|
DIFFERENT from the equity-based "health_pct" in defi --json / prime-summary
|
|
53
58
|
(which uses max_debt = equity * (tier - 1)).
|
|
@@ -60,34 +65,6 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
60
65
|
supplied = g.get("supplied", [])
|
|
61
66
|
borrowed = g.get("borrowed", [])
|
|
62
67
|
health_ratio = g.get("health_ratio", 0) or 0
|
|
63
|
-
# Use precomputed health_pct from defi --json if available (primecli >= 0.5.4)
|
|
64
|
-
precomputed = g.get("health_pct")
|
|
65
|
-
if precomputed is not None:
|
|
66
|
-
# Override health_pct with frontend formula (ignores precomputed value)
|
|
67
|
-
supplied_usd = sum(s.get("usd", 0) or 0 for s in supplied)
|
|
68
|
-
debt_usd = sum(b.get("usd", 0) or 0 for b in borrowed)
|
|
69
|
-
equity = max(supplied_usd - debt_usd, 0.01)
|
|
70
|
-
raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
|
|
71
|
-
symbols = [s.get("symbol", "") for s in supplied]
|
|
72
|
-
has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
|
|
73
|
-
has_lb = any(sym in ("LB_AVAX_USDC", "LB_WAVAX_USDC", "JOE") or "TRADERJOE" in sym.upper() for sym in symbols)
|
|
74
|
-
has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
|
|
75
|
-
# Frontend formula: health_pct = 100 * (1 - debt / (max_mult * equity))
|
|
76
|
-
fe_health = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / (max_mult * equity)))
|
|
77
|
-
fe_max_debt = round(max_mult * equity, 2)
|
|
78
|
-
return {
|
|
79
|
-
"health_pct": round(fe_health, 1),
|
|
80
|
-
"health_ratio": round(health_ratio, 4),
|
|
81
|
-
"supplied_usd": round(supplied_usd, 2),
|
|
82
|
-
"debt_usd": round(debt_usd, 2),
|
|
83
|
-
"equity": round(equity, 2),
|
|
84
|
-
"max_debt": round(max(0, max_mult * equity), 2),
|
|
85
|
-
"raw_usdc": round(raw_usdc, 2),
|
|
86
|
-
"has_gmx": has_gmx,
|
|
87
|
-
"has_lb": has_lb,
|
|
88
|
-
"has_aero": has_aero,
|
|
89
|
-
"action": "computed from defi --json health_pct",
|
|
90
|
-
}
|
|
91
68
|
else:
|
|
92
69
|
supplied = defi_data.get("supplied", [])
|
|
93
70
|
borrowed = defi_data.get("borrowed", [])
|
|
@@ -107,12 +84,48 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
107
84
|
"error": "equity near zero",
|
|
108
85
|
}
|
|
109
86
|
|
|
110
|
-
|
|
87
|
+
# ── Cross-margin formula (per-asset borrowing powers) ─────────────
|
|
88
|
+
if per_asset_powers is not None:
|
|
89
|
+
powers: dict[str, int] = per_asset_powers
|
|
90
|
+
sum_cw = 0.0 # SigmaCw
|
|
91
|
+
sum_bw = 0.0 # SigmaBw
|
|
92
|
+
total_debt = 0.0
|
|
93
|
+
|
|
94
|
+
for s in supplied:
|
|
95
|
+
sym = s.get("symbol", "")
|
|
96
|
+
usd_val = s.get("usd", 0) or 0
|
|
97
|
+
p = powers.get(sym, max_mult)
|
|
98
|
+
pr = p / (p + 1)
|
|
99
|
+
sum_cw += usd_val * pr
|
|
100
|
+
|
|
101
|
+
for b in borrowed:
|
|
102
|
+
sym = b.get("symbol", "")
|
|
103
|
+
usd_val = b.get("usd", 0) or 0
|
|
104
|
+
p = powers.get(sym, max_mult)
|
|
105
|
+
pr = p / (p + 1)
|
|
106
|
+
sum_bw += usd_val * pr
|
|
107
|
+
total_debt += usd_val
|
|
108
|
+
|
|
109
|
+
if sum_cw > 0.01:
|
|
110
|
+
# H = (SigmaCw + SigmaBw - B) / SigmaCw * 100
|
|
111
|
+
health_pct = max(0.0, (sum_cw + sum_bw - total_debt) / sum_cw * 100.0)
|
|
112
|
+
else:
|
|
113
|
+
health_pct = 0.0
|
|
114
|
+
|
|
115
|
+
max_debt = round(max_mult * equity, 2)
|
|
116
|
+
|
|
117
|
+
# ── Simplified formula fallback (uniform borrowing power) ─────────
|
|
118
|
+
else:
|
|
119
|
+
max_debt = round(max_mult * equity, 2)
|
|
120
|
+
|
|
121
|
+
if max_debt > 0.01 and debt_usd >= 0:
|
|
122
|
+
health_pct = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / max_debt))
|
|
123
|
+
else:
|
|
124
|
+
health_pct = 100.0
|
|
111
125
|
|
|
112
|
-
#
|
|
126
|
+
# Common features regardless of formula variant
|
|
113
127
|
raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
|
|
114
128
|
|
|
115
|
-
# Position type detection
|
|
116
129
|
symbols = [s.get("symbol", "") for s in supplied]
|
|
117
130
|
has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
|
|
118
131
|
has_lb = any(
|
|
@@ -122,11 +135,6 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
122
135
|
)
|
|
123
136
|
has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
|
|
124
137
|
|
|
125
|
-
if max_debt > 0.01 and debt_usd >= 0:
|
|
126
|
-
health_pct = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / max_debt))
|
|
127
|
-
else:
|
|
128
|
-
health_pct = 100.0
|
|
129
|
-
|
|
130
138
|
# Center target (50% health): target_debt = max_debt * 0.5
|
|
131
139
|
delta_debt = (max_debt * 0.5) - debt_usd
|
|
132
140
|
|
|
@@ -560,39 +568,52 @@ def run_tick(
|
|
|
560
568
|
deployed_fail += 1
|
|
561
569
|
|
|
562
570
|
elif pos_type == "lb":
|
|
563
|
-
# LB
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
except Exception as e:
|
|
578
|
-
result["error"] = f"lb deposit error: {e}"
|
|
571
|
+
# Detect LB pair from defi data (look for "TraderJoe V2 LB" group)
|
|
572
|
+
lb_pairs = []
|
|
573
|
+
for g in defi_data.get("groups", []):
|
|
574
|
+
if g.get("type") == "TraderJoe V2 LB":
|
|
575
|
+
for item in g.get("items", []):
|
|
576
|
+
label = item.get("label", "")
|
|
577
|
+
m = re.match(r'\[([^\]]+)\]', label)
|
|
578
|
+
if m:
|
|
579
|
+
lb_pairs.append(m.group(1))
|
|
580
|
+
|
|
581
|
+
# Skip if tool doesn't support lb-add (degenprime)
|
|
582
|
+
tool_bn = os.path.basename(tool_path) if tool_path else ""
|
|
583
|
+
if "degenprime" in tool_bn:
|
|
584
|
+
result["action"] = f"lb-add not available on degenprime — leaving ${split_amt:.2f} as USDC"
|
|
579
585
|
deployed_fail += 1
|
|
586
|
+
elif not lb_pairs:
|
|
587
|
+
result["warning"] = f"has_lb=True but no LB pair found in defi data — leaving ${split_amt:.2f} as USDC"
|
|
588
|
+
deployed_fail += 1
|
|
589
|
+
else:
|
|
590
|
+
pair_key = lb_pairs[0]
|
|
591
|
+
try:
|
|
592
|
+
r = subprocess.run(
|
|
593
|
+
[sys.executable, tool_path, "lb-add",
|
|
594
|
+
"--pair", pair_key,
|
|
595
|
+
"--amount-x", "0",
|
|
596
|
+
"--amount-y", f"{split_amt:.2f}",
|
|
597
|
+
"--shape", "spot",
|
|
598
|
+
"--range", "15",
|
|
599
|
+
"--execute"],
|
|
600
|
+
capture_output=True, text=True, timeout=120,
|
|
601
|
+
)
|
|
602
|
+
if r.returncode == 0:
|
|
603
|
+
deployed_ok += 1
|
|
604
|
+
else:
|
|
605
|
+
result["warning"] = f"lb-add failed: {r.stderr[:200]}"
|
|
606
|
+
deployed_fail += 1
|
|
607
|
+
except Exception as e:
|
|
608
|
+
result["error"] = f"lb-add error: {e}"
|
|
609
|
+
deployed_fail += 1
|
|
580
610
|
|
|
581
611
|
elif pos_type == "aero":
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
)
|
|
588
|
-
if r.returncode == 0:
|
|
589
|
-
deployed_ok += 1
|
|
590
|
-
else:
|
|
591
|
-
result["warning"] = f"aerodrome deposit failed: {r.stderr[:200]}"
|
|
592
|
-
deployed_fail += 1
|
|
593
|
-
except Exception as e:
|
|
594
|
-
result["error"] = f"aerodrome deposit error: {e}"
|
|
595
|
-
deployed_fail += 1
|
|
612
|
+
# Aerodrome CL: degenprime has read-only aerodrome-positions,
|
|
613
|
+
# but no deposit/withdraw commands yet (write paths deferred to
|
|
614
|
+
# v2 — on-chain signatures vary by Aerodrome version).
|
|
615
|
+
# Use `degenprime aerodrome-positions` to list your NFT tokenIds.
|
|
616
|
+
result["action"] = f"aero deposit not yet supported (read-only via aerodrome-positions, writes deferred to v2) — leaving ${split_amt:.2f} as USDC"
|
|
596
617
|
|
|
597
618
|
if deployed_ok > 0:
|
|
598
619
|
cooldown_file.write_text(str(int(time.time())))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.9
|
|
4
4
|
Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
|
|
5
5
|
Author: Mnemosyne-quest contributors
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "primecli"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.9"
|
|
8
8
|
description = "Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|