primecli 0.5.2__tar.gz → 0.5.3__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.2 → primecli-0.5.3}/PKG-INFO +2 -3
- {primecli-0.5.2 → primecli-0.5.3}/primecli/arbprime.py +70 -1
- {primecli-0.5.2 → primecli-0.5.3}/primecli/deltaprime.py +91 -13
- {primecli-0.5.2 → primecli-0.5.3}/primecli/health_monitor.py +33 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/PKG-INFO +2 -3
- {primecli-0.5.2 → primecli-0.5.3}/pyproject.toml +2 -2
- {primecli-0.5.2 → primecli-0.5.3}/LICENSE +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/README.md +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli/__init__.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli/degenprime.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/SOURCES.txt +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/dependency_links.txt +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/entry_points.txt +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/requires.txt +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/primecli.egg-info/top_level.txt +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/setup.cfg +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_cross_file_identity.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_gas_pricing.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_health_monitor.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_paraswap_validator.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_redstone_encoding.py +0 -0
- {primecli-0.5.2 → primecli-0.5.3}/tests/test_to_wei_units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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
|
|
@@ -28,7 +28,6 @@ Requires-Dist: eth-account>=0.13
|
|
|
28
28
|
Requires-Dist: eth-keys>=0.5
|
|
29
29
|
Requires-Dist: eth-abi<7,>=5.0
|
|
30
30
|
Requires-Dist: requests>=2.31
|
|
31
|
-
Dynamic: license-file
|
|
32
31
|
|
|
33
32
|
# primecli
|
|
34
33
|
|
|
@@ -57,6 +57,13 @@ prime-summary reports live solvency (health ratio, total value, debt, solvent fl
|
|
|
57
57
|
SolvencyFacetProdArbitrum, read via eth_call with a RedStone price payload appended (falls
|
|
58
58
|
back to balances-only if the gateway is unreachable).
|
|
59
59
|
|
|
60
|
+
NOTE: prime-summary shows TWO health metrics — don't confuse them:
|
|
61
|
+
- "Health ratio (chain)": on-chain getHealthRatio. 1.0 = liquidation, >1.0 = solvent.
|
|
62
|
+
- "Health (Bruno 0-100%)": equity-based, frontend-style. 0% = liquidation, 50% = half
|
|
63
|
+
borrowing power used, 100% = no debt.
|
|
64
|
+
Formula for the latter: equity=supplied-debt, max_debt=equity*(tier-1),
|
|
65
|
+
pct=(max_debt-debt)/max_debt*100. tier=5 (BASIC) or 10 (PREMIUM).
|
|
66
|
+
|
|
60
67
|
Collateral withdrawal is a two-step, time-delayed flow on the Prime Account (there is NO
|
|
61
68
|
instant withdraw of in-account collateral). The savings-pool `withdraw` above is a separate
|
|
62
69
|
two-step intent flow on the pool itself, not the Prime Account. Step 1: `withdraw --pool X
|
|
@@ -1961,6 +1968,49 @@ def gather_lending(w3, account):
|
|
|
1961
1968
|
r["usd"] = None
|
|
1962
1969
|
return out
|
|
1963
1970
|
|
|
1971
|
+
def _compute_bruno_health(data: dict, tier_code: int = 0) -> dict:
|
|
1972
|
+
"""Compute Bruno's 0-100% health from gather_lending data + tier.
|
|
1973
|
+
|
|
1974
|
+
DeltaPrime has *two* health metrics that agents must not confuse:
|
|
1975
|
+
|
|
1976
|
+
1. health_ratio (on-chain, getHealthRatio): 1.0 = liquidation, >1.0 = solvent.
|
|
1977
|
+
This is the raw weighted-collateral / debt ratio from the SolvencyFacet.
|
|
1978
|
+
|
|
1979
|
+
2. bruno_pct (equity-based, 0-100%): the scale used in the DeltaPrime frontend
|
|
1980
|
+
and the account-health-monitor cron. 0% = liquidation, 100% = no debt.
|
|
1981
|
+
Formula:
|
|
1982
|
+
equity = supplied_usd - debt_usd
|
|
1983
|
+
max_mult = 10 if PREMIUM tier else 5 if BASIC
|
|
1984
|
+
max_debt = equity * (max_mult - 1)
|
|
1985
|
+
bruno_pct = (max_debt - debt_usd) / max_debt * 100
|
|
1986
|
+
|
|
1987
|
+
Returns dict with keys: bruno_pct, supplied_usd, debt_usd, equity, max_debt,
|
|
1988
|
+
tier_label, or error.
|
|
1989
|
+
"""
|
|
1990
|
+
supplied_usd = sum(r.get("usd", 0) or 0 for r in data.get("supplied", []))
|
|
1991
|
+
debt_usd = sum(r.get("usd", 0) or 0 for r in data.get("borrowed", []))
|
|
1992
|
+
equity = supplied_usd - debt_usd
|
|
1993
|
+
tier_labels = {0: "BASIC", 1: "PREMIUM", 2: "_NON_EXISTENT"}
|
|
1994
|
+
tier_label = tier_labels.get(tier_code, str(tier_code))
|
|
1995
|
+
max_mult = {0: 5, 1: 10}.get(tier_code, 5)
|
|
1996
|
+
|
|
1997
|
+
if equity <= 0.01:
|
|
1998
|
+
return {"bruno_pct": 0.0, "supplied_usd": round(supplied_usd, 2),
|
|
1999
|
+
"debt_usd": round(debt_usd, 2), "equity": round(equity, 2),
|
|
2000
|
+
"max_debt": 0.0, "tier": tier_label, "error": "equity near zero"}
|
|
2001
|
+
|
|
2002
|
+
max_debt = equity * (max_mult - 1)
|
|
2003
|
+
if max_debt > 0 and debt_usd >= 0:
|
|
2004
|
+
bruno_pct = (max_debt - min(debt_usd, max_debt)) / max_debt * 100
|
|
2005
|
+
bruno_pct = max(0.0, min(100.0, bruno_pct))
|
|
2006
|
+
else:
|
|
2007
|
+
bruno_pct = 100.0
|
|
2008
|
+
|
|
2009
|
+
return {"bruno_pct": round(bruno_pct, 1), "supplied_usd": round(supplied_usd, 2),
|
|
2010
|
+
"debt_usd": round(debt_usd, 2), "equity": round(equity, 2),
|
|
2011
|
+
"max_debt": round(max_debt, 2), "tier": tier_label}
|
|
2012
|
+
|
|
2013
|
+
|
|
1964
2014
|
def cmd_prime_summary():
|
|
1965
2015
|
w3 = get_w3()
|
|
1966
2016
|
acct = get_account()
|
|
@@ -2004,7 +2054,23 @@ def cmd_prime_summary():
|
|
|
2004
2054
|
# gather_lending nulls the ratio when debt is negligible (the raw value is
|
|
2005
2055
|
# astronomically large there); render that as ">1000" rather than a junk number.
|
|
2006
2056
|
ratio_str = ">1000.00 (negligible debt)" if ratio is None else f"{ratio:.4f}"
|
|
2007
|
-
print(f" Health ratio:
|
|
2057
|
+
print(f" Health ratio (chain): {ratio_str} (>1.0 = solvent, 1.0 = liquidation)")
|
|
2058
|
+
# ─── Bruno's 0-100% health (equity-based, uses tier multiplier) ───
|
|
2059
|
+
# Different from health_ratio! See _compute_bruno_health docstring.
|
|
2060
|
+
# Get tier from the Prime Account (oracle-free view)
|
|
2061
|
+
try:
|
|
2062
|
+
tier_info = gather_prime_tier(w3, acct, account)
|
|
2063
|
+
tier_code = tier_info.get("tier_code", 0)
|
|
2064
|
+
except Exception:
|
|
2065
|
+
tier_code = 0
|
|
2066
|
+
bh = _compute_bruno_health(data, tier_code)
|
|
2067
|
+
if "error" not in bh:
|
|
2068
|
+
print(f" Health (Bruno 0-100%): {bh['bruno_pct']:.1f}%")
|
|
2069
|
+
print(f" (supplied=${bh['supplied_usd']:.2f}, debt=${bh['debt_usd']:.2f},"
|
|
2070
|
+
f" equity=${bh['equity']:.2f}, max_debt=${bh['max_debt']:.2f}, {bh['tier']})")
|
|
2071
|
+
print(f" 0%=liquidation 50%=half borrowing power used 100%=no debt")
|
|
2072
|
+
else:
|
|
2073
|
+
print(f" Health (Bruno 0-100%): N/A ({bh['error']})")
|
|
2008
2074
|
print(f" Solvent: {'yes' if data['solvent'] else 'NO — liquidatable'}")
|
|
2009
2075
|
else:
|
|
2010
2076
|
print(f" Health/solvency: RedStone fetch/call failed ({data.get('solvency_error', 'error')}); "
|
|
@@ -5005,9 +5071,12 @@ def gather_defi() -> dict:
|
|
|
5005
5071
|
result["total_usd"] = lending["total_value_usd"]
|
|
5006
5072
|
result["health_ratio"] = lending["health_ratio"]
|
|
5007
5073
|
result["solvent"] = lending["solvent"]
|
|
5074
|
+
# Compute Bruno's 0-100% health from lending data + tier
|
|
5075
|
+
result["bruno_pct"] = _compute_bruno_health(lending, tier.get("tier_code", 0)).get("bruno_pct")
|
|
5008
5076
|
if lending["supplied"] or lending["borrowed"]:
|
|
5009
5077
|
result["groups"].append({
|
|
5010
5078
|
"type": "Lending / Leverage", "health_ratio": lending["health_ratio"],
|
|
5079
|
+
"bruno_pct": result["bruno_pct"],
|
|
5011
5080
|
"supplied": [{"symbol": r["symbol"], "balance": r["balance"], "usd": r.get("usd")}
|
|
5012
5081
|
for r in lending["supplied"]],
|
|
5013
5082
|
"borrowed": [{"symbol": r["symbol"], "balance": r["balance"], "usd": r.get("usd")}
|
|
@@ -50,6 +50,13 @@ prime-summary reports live solvency (health ratio, total value, debt, solvent fl
|
|
|
50
50
|
SolvencyFacetProdAvalanche, read via eth_call with a RedStone price payload appended (falls
|
|
51
51
|
back to balances-only if the gateway is unreachable).
|
|
52
52
|
|
|
53
|
+
NOTE: prime-summary shows TWO health metrics — don't confuse them:
|
|
54
|
+
- "Health ratio (chain)": on-chain getHealthRatio. 1.0 = liquidation, >1.0 = solvent.
|
|
55
|
+
- "Health (Bruno 0-100%)": equity-based, frontend-style. 0% = liquidation, 50% = half
|
|
56
|
+
borrowing power used, 100% = no debt.
|
|
57
|
+
Formula for the latter: equity=supplied-debt, max_debt=equity*(tier-1),
|
|
58
|
+
pct=(max_debt-debt)/max_debt*100. tier=5 (BASIC) or 10 (PREMIUM).
|
|
59
|
+
|
|
53
60
|
Collateral withdrawal is a two-step, time-delayed flow on the Prime Account (there is NO
|
|
54
61
|
instant withdraw of in-account collateral). The savings-pool `withdraw` above is a separate
|
|
55
62
|
two-step intent flow on the pool itself, not the Prime Account. Step 1: `withdraw --pool X
|
|
@@ -658,33 +665,42 @@ def _tx_gas_price(w3) -> int:
|
|
|
658
665
|
|
|
659
666
|
def _set_gas_price(w3, tx_dict):
|
|
660
667
|
"""Set appropriate gas price fields for the chain, replacing the legacy gasPrice approach.
|
|
661
|
-
On EIP-1559 chains (Arbitrum, Base): sets maxFeePerGas +
|
|
662
|
-
base-fee hedge (base + prio + 1 gwei buffer).
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
668
|
+
On EIP-1559 chains (Arbitrum, Base, Avalanche post-Etna): sets maxFeePerGas +
|
|
669
|
+
maxPriorityFeePerGas with a 2x base-fee hedge (base + prio + 1 gwei buffer).
|
|
670
|
+
Falls back to legacy gasPrice only if the tx dict already lacks EIP-1559 fields
|
|
671
|
+
and the chain doesn't support max_priority_fee.
|
|
672
|
+
(25 gwei was the pre-Etna C-chain minimum; ACP-125 (Dec 2024) lowered the min base
|
|
673
|
+
fee to 1 nAVAX — base now sits at ~0.01 nAVAX, so a 25 gwei floor overpaid ~2500x
|
|
674
|
+
and inflated the upfront balance requirement past small EOAs.)"""
|
|
675
|
+
# If build_transaction already set EIP-1559 fields, don't touch them
|
|
676
|
+
if "maxFeePerGas" in tx_dict or "maxPriorityFeePerGas" in tx_dict:
|
|
677
|
+
tx_dict.pop("gasPrice", None)
|
|
678
|
+
return
|
|
667
679
|
tx_dict.pop("gasPrice", None)
|
|
668
|
-
|
|
680
|
+
try:
|
|
669
681
|
base = w3.eth.gas_price
|
|
670
682
|
prio = w3.eth.max_priority_fee
|
|
671
683
|
tx_dict["maxFeePerGas"] = max(int(base * 2), base + prio + 10**9)
|
|
672
684
|
tx_dict["maxPriorityFeePerGas"] = prio
|
|
673
|
-
|
|
685
|
+
except Exception:
|
|
686
|
+
# Legacy chain — use gasPrice instead
|
|
674
687
|
tx_dict["gasPrice"] = max(int(w3.eth.gas_price * 2), 1 * 10**9)
|
|
675
688
|
|
|
676
689
|
def _set_gas_price_for(chain_id, w3, tx_dict):
|
|
677
690
|
"""Set gas fields for an EXPLICIT chain_id rather than the module CHAIN_ID. Needed by
|
|
678
691
|
cross-chain flows (prime-bridge) where a tx may target Avalanche or Arbitrum regardless
|
|
679
|
-
of which tool built it.
|
|
680
|
-
|
|
692
|
+
of which tool built it."""
|
|
693
|
+
# If build_transaction already set EIP-1559 fields, don't touch them
|
|
694
|
+
if "maxFeePerGas" in tx_dict or "maxPriorityFeePerGas" in tx_dict:
|
|
695
|
+
tx_dict.pop("gasPrice", None)
|
|
696
|
+
return
|
|
681
697
|
tx_dict.pop("gasPrice", None)
|
|
682
|
-
|
|
698
|
+
try:
|
|
683
699
|
base = w3.eth.gas_price
|
|
684
700
|
prio = w3.eth.max_priority_fee
|
|
685
701
|
tx_dict["maxFeePerGas"] = max(int(base * 2), base + prio + 10**9)
|
|
686
702
|
tx_dict["maxPriorityFeePerGas"] = prio
|
|
687
|
-
|
|
703
|
+
except Exception:
|
|
688
704
|
tx_dict["gasPrice"] = max(int(w3.eth.gas_price * 2), 1 * 10**9)
|
|
689
705
|
|
|
690
706
|
def _read_env_var(path, var):
|
|
@@ -1979,6 +1995,49 @@ def gather_lending(w3, account):
|
|
|
1979
1995
|
r["usd"] = None
|
|
1980
1996
|
return out
|
|
1981
1997
|
|
|
1998
|
+
def _compute_bruno_health(data: dict, tier_code: int = 0) -> dict:
|
|
1999
|
+
"""Compute Bruno's 0-100% health from gather_lending data + tier.
|
|
2000
|
+
|
|
2001
|
+
DeltaPrime has *two* health metrics that agents must not confuse:
|
|
2002
|
+
|
|
2003
|
+
1. health_ratio (on-chain, getHealthRatio): 1.0 = liquidation, >1.0 = solvent.
|
|
2004
|
+
This is the raw weighted-collateral / debt ratio from the SolvencyFacet.
|
|
2005
|
+
|
|
2006
|
+
2. bruno_pct (equity-based, 0-100%): the scale used in the DeltaPrime frontend
|
|
2007
|
+
and the account-health-monitor cron. 0% = liquidation, 100% = no debt.
|
|
2008
|
+
Formula:
|
|
2009
|
+
equity = supplied_usd - debt_usd
|
|
2010
|
+
max_mult = 10 if PREMIUM tier else 5 if BASIC
|
|
2011
|
+
max_debt = equity * (max_mult - 1)
|
|
2012
|
+
bruno_pct = (max_debt - debt_usd) / max_debt * 100
|
|
2013
|
+
|
|
2014
|
+
Returns dict with keys: bruno_pct, supplied_usd, debt_usd, equity, max_debt,
|
|
2015
|
+
tier_label, or error.
|
|
2016
|
+
"""
|
|
2017
|
+
supplied_usd = sum(r.get("usd", 0) or 0 for r in data.get("supplied", []))
|
|
2018
|
+
debt_usd = sum(r.get("usd", 0) or 0 for r in data.get("borrowed", []))
|
|
2019
|
+
equity = supplied_usd - debt_usd
|
|
2020
|
+
tier_labels = {0: "BASIC", 1: "PREMIUM", 2: "_NON_EXISTENT"}
|
|
2021
|
+
tier_label = tier_labels.get(tier_code, str(tier_code))
|
|
2022
|
+
max_mult = {0: 5, 1: 10}.get(tier_code, 5)
|
|
2023
|
+
|
|
2024
|
+
if equity <= 0.01:
|
|
2025
|
+
return {"bruno_pct": 0.0, "supplied_usd": round(supplied_usd, 2),
|
|
2026
|
+
"debt_usd": round(debt_usd, 2), "equity": round(equity, 2),
|
|
2027
|
+
"max_debt": 0.0, "tier": tier_label, "error": "equity near zero"}
|
|
2028
|
+
|
|
2029
|
+
max_debt = equity * (max_mult - 1)
|
|
2030
|
+
if max_debt > 0 and debt_usd >= 0:
|
|
2031
|
+
bruno_pct = (max_debt - min(debt_usd, max_debt)) / max_debt * 100
|
|
2032
|
+
bruno_pct = max(0.0, min(100.0, bruno_pct))
|
|
2033
|
+
else:
|
|
2034
|
+
bruno_pct = 100.0
|
|
2035
|
+
|
|
2036
|
+
return {"bruno_pct": round(bruno_pct, 1), "supplied_usd": round(supplied_usd, 2),
|
|
2037
|
+
"debt_usd": round(debt_usd, 2), "equity": round(equity, 2),
|
|
2038
|
+
"max_debt": round(max_debt, 2), "tier": tier_label}
|
|
2039
|
+
|
|
2040
|
+
|
|
1982
2041
|
def cmd_prime_summary():
|
|
1983
2042
|
w3 = get_w3()
|
|
1984
2043
|
acct = get_account()
|
|
@@ -2022,7 +2081,23 @@ def cmd_prime_summary():
|
|
|
2022
2081
|
# gather_lending nulls the ratio when debt is negligible (the raw value is
|
|
2023
2082
|
# astronomically large there); render that as ">1000" rather than a junk number.
|
|
2024
2083
|
ratio_str = ">1000.00 (negligible debt)" if ratio is None else f"{ratio:.4f}"
|
|
2025
|
-
print(f" Health ratio:
|
|
2084
|
+
print(f" Health ratio (chain): {ratio_str} (>1.0 = solvent, 1.0 = liquidation)")
|
|
2085
|
+
# ─── Bruno's 0-100% health (equity-based, uses tier multiplier) ───
|
|
2086
|
+
# Different from health_ratio! See _compute_bruno_health docstring.
|
|
2087
|
+
# Get tier from the Prime Account (oracle-free view)
|
|
2088
|
+
try:
|
|
2089
|
+
tier_info = gather_prime_tier(w3, acct, account)
|
|
2090
|
+
tier_code = tier_info.get("tier_code", 0)
|
|
2091
|
+
except Exception:
|
|
2092
|
+
tier_code = 0
|
|
2093
|
+
bh = _compute_bruno_health(data, tier_code)
|
|
2094
|
+
if "error" not in bh:
|
|
2095
|
+
print(f" Health (Bruno 0-100%): {bh['bruno_pct']:.1f}%")
|
|
2096
|
+
print(f" (supplied=${bh['supplied_usd']:.2f}, debt=${bh['debt_usd']:.2f},"
|
|
2097
|
+
f" equity=${bh['equity']:.2f}, max_debt=${bh['max_debt']:.2f}, {bh['tier']})")
|
|
2098
|
+
print(f" 0%=liquidation 50%=half borrowing power used 100%=no debt")
|
|
2099
|
+
else:
|
|
2100
|
+
print(f" Health (Bruno 0-100%): N/A ({bh['error']})")
|
|
2026
2101
|
print(f" Solvent: {'yes' if data['solvent'] else 'NO — liquidatable'}")
|
|
2027
2102
|
else:
|
|
2028
2103
|
print(f" Health/solvency: RedStone fetch/call failed ({data.get('solvency_error', 'error')}); "
|
|
@@ -4928,9 +5003,12 @@ def gather_defi() -> dict:
|
|
|
4928
5003
|
result["total_usd"] = lending["total_value_usd"]
|
|
4929
5004
|
result["health_ratio"] = lending["health_ratio"]
|
|
4930
5005
|
result["solvent"] = lending["solvent"]
|
|
5006
|
+
# Compute Bruno's 0-100% health from lending data + tier
|
|
5007
|
+
result["bruno_pct"] = _compute_bruno_health(lending, tier.get("tier_code", 0)).get("bruno_pct")
|
|
4931
5008
|
if lending["supplied"] or lending["borrowed"]:
|
|
4932
5009
|
result["groups"].append({
|
|
4933
5010
|
"type": "Lending / Leverage", "health_ratio": lending["health_ratio"],
|
|
5011
|
+
"bruno_pct": result["bruno_pct"],
|
|
4934
5012
|
"supplied": [{"symbol": r["symbol"], "balance": r["balance"], "usd": r.get("usd")}
|
|
4935
5013
|
for r in lending["supplied"]],
|
|
4936
5014
|
"borrowed": [{"symbol": r["symbol"], "balance": r["balance"], "usd": r.get("usd")}
|
|
@@ -39,11 +39,19 @@ TIER_MAX = {"basic": 5, "premium": 10}
|
|
|
39
39
|
def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
40
40
|
"""Compute Bruno's 0-100% health scale from defi --json data.
|
|
41
41
|
|
|
42
|
+
PREFERS the precomputed ``bruno_pct`` from defi --json (which primecli now
|
|
43
|
+
includes as of 2026-06-04), falling back to manual calculation when absent.
|
|
44
|
+
|
|
42
45
|
Formula:
|
|
43
46
|
equity = total_supplied_usd - total_debt_usd
|
|
44
47
|
max_debt = equity * (tier - 1) # PREMIUM=10, BASIC=5
|
|
45
48
|
health% = (max_debt - debt) / max_debt * 100
|
|
46
49
|
|
|
50
|
+
This is DIFFERENT from getHealthRatio (the on-chain ratio where 1.0 = liquidation).
|
|
51
|
+
Do NOT convert between the two. The ``health_ratio`` metric is the on-chain value
|
|
52
|
+
(1.0=liquidation, >1.0=solvent). The ``bruno_pct`` is the equity-based frontend
|
|
53
|
+
measurement (0%=liquidation, 50%=half borrowing power used, 100%=no debt).
|
|
54
|
+
|
|
47
55
|
Returns dict with health metrics or error.
|
|
48
56
|
"""
|
|
49
57
|
# Parse groups (DeltaPrime format) or flat format (DegenPrime)
|
|
@@ -53,6 +61,31 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
53
61
|
supplied = g.get("supplied", [])
|
|
54
62
|
borrowed = g.get("borrowed", [])
|
|
55
63
|
health_ratio = g.get("health_ratio", 0) or 0
|
|
64
|
+
# Use precomputed bruno_pct from defi --json if available (primecli >= 0.5.0)
|
|
65
|
+
precomputed = g.get("bruno_pct")
|
|
66
|
+
if precomputed is not None:
|
|
67
|
+
# Early return: precomputed value exists, enrich with detail fields
|
|
68
|
+
supplied_usd = sum(s.get("usd", 0) or 0 for s in supplied)
|
|
69
|
+
debt_usd = sum(b.get("usd", 0) or 0 for b in borrowed)
|
|
70
|
+
equity = supplied_usd - debt_usd
|
|
71
|
+
raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
|
|
72
|
+
symbols = [s.get("symbol", "") for s in supplied]
|
|
73
|
+
has_gmx = any("GM_" in sym for sym in symbols)
|
|
74
|
+
has_lb = any(sym in ("LB_AVAX_USDC", "LB_WAVAX_USDC", "JOE") or "TRADERJOE" in sym.upper() for sym in symbols)
|
|
75
|
+
has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
|
|
76
|
+
return {
|
|
77
|
+
"bruno_pct": float(precomputed),
|
|
78
|
+
"health_ratio": round(health_ratio, 4),
|
|
79
|
+
"supplied_usd": round(supplied_usd, 2),
|
|
80
|
+
"debt_usd": round(debt_usd, 2),
|
|
81
|
+
"equity": round(equity, 2),
|
|
82
|
+
"max_debt": round(max(0, equity * (max_mult - 1)), 2),
|
|
83
|
+
"raw_usdc": round(raw_usdc, 2),
|
|
84
|
+
"has_gmx": has_gmx,
|
|
85
|
+
"has_lb": has_lb,
|
|
86
|
+
"has_aero": has_aero,
|
|
87
|
+
"action": "computed from defi --json bruno_pct",
|
|
88
|
+
}
|
|
56
89
|
else:
|
|
57
90
|
supplied = defi_data.get("supplied", [])
|
|
58
91
|
borrowed = defi_data.get("borrowed", [])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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
|
|
@@ -28,7 +28,6 @@ Requires-Dist: eth-account>=0.13
|
|
|
28
28
|
Requires-Dist: eth-keys>=0.5
|
|
29
29
|
Requires-Dist: eth-abi<7,>=5.0
|
|
30
30
|
Requires-Dist: requests>=2.31
|
|
31
|
-
Dynamic: license-file
|
|
32
31
|
|
|
33
32
|
# primecli
|
|
34
33
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=68", "wheel"]
|
|
2
|
+
requires = ["setuptools>=68,<73", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "primecli"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.3"
|
|
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
|