primecli 0.8.0__tar.gz → 0.8.1__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.
Files changed (24) hide show
  1. {primecli-0.8.0 → primecli-0.8.1}/PKG-INFO +2 -2
  2. {primecli-0.8.0 → primecli-0.8.1}/README.md +1 -1
  3. {primecli-0.8.0 → primecli-0.8.1}/primecli/degenprime.py +104 -82
  4. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/PKG-INFO +2 -2
  5. {primecli-0.8.0 → primecli-0.8.1}/pyproject.toml +1 -1
  6. {primecli-0.8.0 → primecli-0.8.1}/LICENSE +0 -0
  7. {primecli-0.8.0 → primecli-0.8.1}/primecli/__init__.py +0 -0
  8. {primecli-0.8.0 → primecli-0.8.1}/primecli/arbprime.py +0 -0
  9. {primecli-0.8.0 → primecli-0.8.1}/primecli/deltaprime.py +0 -0
  10. {primecli-0.8.0 → primecli-0.8.1}/primecli/health_monitor.py +0 -0
  11. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/SOURCES.txt +0 -0
  12. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/dependency_links.txt +0 -0
  13. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/entry_points.txt +0 -0
  14. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/requires.txt +0 -0
  15. {primecli-0.8.0 → primecli-0.8.1}/primecli.egg-info/top_level.txt +0 -0
  16. {primecli-0.8.0 → primecli-0.8.1}/setup.cfg +0 -0
  17. {primecli-0.8.0 → primecli-0.8.1}/tests/test_cross_file_identity.py +0 -0
  18. {primecli-0.8.0 → primecli-0.8.1}/tests/test_gas_limit.py +0 -0
  19. {primecli-0.8.0 → primecli-0.8.1}/tests/test_gas_pricing.py +0 -0
  20. {primecli-0.8.0 → primecli-0.8.1}/tests/test_health_meter.py +0 -0
  21. {primecli-0.8.0 → primecli-0.8.1}/tests/test_health_monitor.py +0 -0
  22. {primecli-0.8.0 → primecli-0.8.1}/tests/test_paraswap_validator.py +0 -0
  23. {primecli-0.8.0 → primecli-0.8.1}/tests/test_redstone_encoding.py +0 -0
  24. {primecli-0.8.0 → primecli-0.8.1}/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.8.0
3
+ Version: 0.8.1
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
@@ -47,7 +47,7 @@ Built for agent use:
47
47
  - RedStone-signed solvency math handled internally, with a regression test pinning the half-boundary `toFixed(8)` encoding.
48
48
  - ParaSwap calldata validated client-side against the on-chain executor allowlist before broadcast.
49
49
 
50
- **Current version:** 0.8.0 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
50
+ **Current version:** 0.8.1 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
51
51
 
52
52
  > **Breaking change in 0.5.0:** there is no longer a default signing key. Earlier versions silently fell back to a baked-in agent when no key was configured; that fallback has been removed. With no key configured, every command now fails closed with `No signing key found...`. Set a key explicitly (see [Configuration](#configuration)).
53
53
 
@@ -16,7 +16,7 @@ Built for agent use:
16
16
  - RedStone-signed solvency math handled internally, with a regression test pinning the half-boundary `toFixed(8)` encoding.
17
17
  - ParaSwap calldata validated client-side against the on-chain executor allowlist before broadcast.
18
18
 
19
- **Current version:** 0.8.0 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
19
+ **Current version:** 0.8.1 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
20
20
 
21
21
  > **Breaking change in 0.5.0:** there is no longer a default signing key. Earlier versions silently fell back to a baked-in agent when no key was configured; that fallback has been removed. With no key configured, every command now fails closed with `No signing key found...`. Set a key explicitly (see [Configuration](#configuration)).
22
22
 
@@ -27,7 +27,7 @@ Usage:
27
27
  degenprime cancel-withdrawal --pool usdc --index N [--execute]
28
28
  degenprime aerodrome-positions
29
29
  degenprime aero-add-liquidity --pool weth-usdc-100 --amount-weth 0.05 --amount-usdc 100 [--slippage 1] [--execute]
30
- degenprime aero-remove-liquidity --token-id N [--percentage 100] [--execute]
30
+ degenprime aero-remove-liquidity --token-id N [--token-id M ...] [--execute] (full close only)
31
31
  degenprime aero-collect-fees --token-id N [--execute]
32
32
 
33
33
  Configuration (env vars):
@@ -85,6 +85,13 @@ the Aerodrome Slipstream NonfungiblePositionManager through the Degen Account's
85
85
  AerodromeFacet wrapper functions. The facet selectors were determined via on-chain
86
86
  probing (Diamond Loupe) of the smart-loan diamond; function names are inferred from
87
87
  their parameter layouts and revert signatures.
88
+
89
+ aero-remove-liquidity fully closes one or more staked positions in a single call via
90
+ batchRemoveStakedLiquidityAerodrome(uint256[]) (selector 0x27bed82e): per tokenId it
91
+ unstakes from the gauge, removes all liquidity, collects fees, and burns the NFT. There
92
+ is no partial/percentage decrease on this path — it always closes 100%. The call is
93
+ solvency-gated, so the calldata carries a RedStone payload (verified byte-exact against
94
+ the manual close 0x0d65...0a50).
88
95
  """
89
96
 
90
97
  import json, os, sys, time, base64
@@ -208,14 +215,13 @@ AERODROME_NPM = "0x827922686190790b37229fd06084350E74485b72"
208
215
  # 6f2845cd getOwnedStakedAerodromeTokenIds()
209
216
  # b6626971 getPositionCompositionSimplified(uint256) -> (address,address,uint256,uint256)
210
217
  # 121350b3 (view, takes uint256 — detailed position info, unknown return)
211
- # 27bed82e (write, onlyOwner, takes MintParams-like tuple — inferred mint/add)
218
+ # 27bed82e batchRemoveStakedLiquidityAerodrome(uint256[]) full close per id
219
+ # (verified byte-exact vs manual close 0x0d65...0a50, 2026-06-14)
212
220
  # 2c710777 (write, onlyOwner, takes IncreaseLiquidityParams-like tuple — inferred increase)
213
- # ca15558b (write, takes DecreaseLiquidityParams tuple 5-field — matches decreaseLiquidity)
214
221
  # 92b5a47e (write, takes uint256 tokenId, checks position exists — burn/collect)
215
222
  # 46daca2c (write, onlyOwnerOrLiquidator — emergency withdrawal)
216
223
  AERODROME_SEL_MINT = bytes.fromhex("f32f1e56") # mintAndStakeLiquidityAerodrome
217
224
  AERODROME_SEL_INCREASE = bytes.fromhex("2c710777") # inferred: increaseLiquidity
218
- AERODROME_SEL_DECREASE = bytes.fromhex("cb16b6c6") # decreaseAerodromeLiquidity
219
225
  AERODROME_SEL_BURN = bytes.fromhex("27bed82e") # batchRemoveStakedLiquidityAerodrome
220
226
  AERODROME_SEL_COLLECT = bytes.fromhex("887e4b7e") # collectAerodromeFees
221
227
 
@@ -801,19 +807,6 @@ PRIME_ACCOUNT_ABI = [
801
807
  ]}],
802
808
  "name": "mintAerodrome", "outputs": [],
803
809
  "stateMutability": "nonpayable", "type": "function"},
804
- # decreaseAerodromeLiquidity: wraps NPM.decreaseLiquidity(DecreaseLiquidityParams).
805
- # DecreaseLiquidityParams = (uint256 tokenId, uint128 liquidity,
806
- # uint256 amount0Min, uint256 amount1Min, uint256 deadline).
807
- # Selector: 0xca15558b (probed — accepts 5-field tuple matching decrease layout).
808
- {"inputs": [{"name": "params", "type": "tuple", "components": [
809
- {"name": "tokenId", "type": "uint256"},
810
- {"name": "liquidity", "type": "uint128"},
811
- {"name": "amount0Min", "type": "uint256"},
812
- {"name": "amount1Min", "type": "uint256"},
813
- {"name": "deadline", "type": "uint256"}
814
- ]}],
815
- "name": "decreaseAerodromeLiquidity", "outputs": [],
816
- "stateMutability": "nonpayable", "type": "function"},
817
810
  # burnAerodromePosition: wraps NPM.burn(uint256). tokenId must have 0 liquidity
818
811
  # and all fees collected.
819
812
  # Selector: 0x92b5a47e (probed — takes uint256, checks position exists via NPM.positions).
@@ -3133,10 +3126,17 @@ def cmd_aerodrome_positions():
3133
3126
  token0, token1, tick_lower, tick_upper, liq = pos
3134
3127
  sym0 = _resolve_token_symbol(w3, token0)
3135
3128
  sym1 = _resolve_token_symbol(w3, token1)
3136
- price_lower = 1.0001 ** tick_lower
3137
- price_upper = 1.0001 ** tick_upper
3138
- print(f" [{tid}] {sym0}/{sym1} ticks=[{tick_lower}, {tick_upper}]"
3139
- f" liq={liq} price_range=[{price_lower:.4f}, {price_upper:.4f}]")
3129
+ # Human price = token1 per token0 = 1.0001**tick * 10**(dec0 - dec1).
3130
+ dec0 = _resolve_token_decimals(w3, token0)
3131
+ dec1 = _resolve_token_decimals(w3, token1)
3132
+ if dec0 is not None and dec1 is not None:
3133
+ scale = 10 ** (dec0 - dec1)
3134
+ price_lower = 1.0001 ** tick_lower * scale
3135
+ price_upper = 1.0001 ** tick_upper * scale
3136
+ print(f" [{tid}] {sym0}/{sym1} ticks=[{tick_lower}, {tick_upper}]"
3137
+ f" liq={liq} price_range=[{price_lower:.6g}, {price_upper:.6g}] ({sym1}/{sym0})")
3138
+ else:
3139
+ print(f" [{tid}] {sym0}/{sym1} ticks=[{tick_lower}, {tick_upper}] liq={liq}")
3140
3140
  print(" Manage on Aerodrome UI: https://aerodrome.finance/positions")
3141
3141
 
3142
3142
  def _aero_read_position(w3, token_id: int):
@@ -3197,6 +3197,25 @@ def _resolve_token_symbol(w3, addr: str) -> str:
3197
3197
  pass
3198
3198
  return addr[:10] + "..."
3199
3199
 
3200
+ _ERC20_DECIMALS_ABI = json.dumps([{"inputs": [], "name": "decimals",
3201
+ "outputs": [{"type": "uint8"}], "stateMutability": "view", "type": "function"}])
3202
+
3203
+ def _resolve_token_decimals(w3, addr: str):
3204
+ """Token decimals from the static pool maps, falling back to an on-chain
3205
+ decimals() read. Returns the int decimals or None if it can't be determined."""
3206
+ addr_lower = addr.lower()
3207
+ for cfg in AERODROME_POOLS.values():
3208
+ if cfg["token0"].lower() == addr_lower:
3209
+ return cfg["decimals0"]
3210
+ if cfg["token1"].lower() == addr_lower:
3211
+ return cfg["decimals1"]
3212
+ try:
3213
+ c = w3.eth.contract(address=Web3.to_checksum_address(addr),
3214
+ abi=json.loads(_ERC20_DECIMALS_ABI))
3215
+ return c.functions.decimals().call()
3216
+ except Exception:
3217
+ return None
3218
+
3200
3219
  # ─── Aerodrome Write Commands ────────────────────────────────────────────────
3201
3220
 
3202
3221
  # Helper: get Aerodrome CL pool address from the factory's getPool (authoritative).
@@ -3307,12 +3326,6 @@ def _aero_mint_params(pool_cfg: dict, amount0_wei: int, amount1_wei: int,
3307
3326
  0, 0, 0, # word11-13: zero (sqrtPriceX96=0 / bools false)
3308
3327
  )
3309
3328
 
3310
- # Helper: build DecreaseLiquidityParams tuple (5 fields).
3311
- def _aero_decrease_params(token_id: int, liquidity: int,
3312
- amount0_min: int, amount1_min: int) -> tuple:
3313
- deadline = int(time.time()) + 1800
3314
- return (token_id, liquidity, amount0_min, amount1_min, deadline)
3315
-
3316
3329
  # Helper: compute tick range around a desired centre price.
3317
3330
  def _aero_tick_range(tick_spacing: int, centre_price: float = None,
3318
3331
  width_pct: float = 2.0, pool_tick: int = None) -> tuple:
@@ -3468,15 +3481,31 @@ def cmd_aero_add_liquidity(pool_key: str, amount0: float = None,
3468
3481
  ok = receipt["status"] == 1
3469
3482
 
3470
3483
 
3471
- def cmd_aero_remove_liquidity(token_id: int, percentage: float = 100.0,
3484
+ def cmd_aero_remove_liquidity(token_ids, percentage: float = 100.0,
3472
3485
  execute: bool = False):
3473
- """Remove liquidity from an Aerodrome Slipstream position owned by the
3474
- Degen Account. Decreases liquidity by `percentage` of the current position
3475
- size (default 100% = full close). The position NFT remains; burn it
3476
- separately after all liquidity is removed and fees collected.
3486
+ """Fully close one or more staked Aerodrome Slipstream positions owned by the
3487
+ Degen Account, via batchRemoveStakedLiquidityAerodrome(uint256[]) on the
3488
+ AerodromeFacet (selector 0x27bed82e). This single call does the FULL unwind per
3489
+ tokenId: unstake from the gauge + remove all liquidity + collect fees + burn the
3490
+ NFT (the manual reference close 0x0d65... emitted 41 logs doing exactly this).
3491
+
3492
+ There is NO partial/percentage decrease on this path — it always closes 100%.
3493
+ The call is remainsSolvent-gated, so the calldata carries a RedStone signed-price
3494
+ payload (same construction as the mint+stake path)."""
3495
+ if percentage < 100:
3496
+ print(f" Partial removal ({percentage}%) is not supported on this path — "
3497
+ f"batchRemoveStakedLiquidityAerodrome fully closes each position "
3498
+ f"(unstake + remove + collect + burn). Re-run without --percentage "
3499
+ f"(or with --percentage 100) to fully close.")
3500
+ return
3501
+
3502
+ if isinstance(token_ids, int):
3503
+ token_ids = [token_ids]
3504
+ token_ids = [int(t) for t in token_ids]
3505
+ if not token_ids:
3506
+ print(" No tokenIds supplied.")
3507
+ return
3477
3508
 
3478
- The facet wraps NPM.decreaseLiquidity. No RedStone payload needed
3479
- (the decrease path is NOT remainsSolvent — same as TJ lb-remove)."""
3480
3509
  w3 = get_w3()
3481
3510
  acct = get_account()
3482
3511
  print(f"Wallet: {acct.address}")
@@ -3487,41 +3516,33 @@ def cmd_aero_remove_liquidity(token_id: int, percentage: float = 100.0,
3487
3516
  account = w3.eth.contract(address=Web3.to_checksum_address(pa), abi=PRIME_ACCOUNT_ABI)
3488
3517
  print(f"Degen Account: {pa}")
3489
3518
 
3490
- # Read the position's current liquidity from NPM.positions() correct for
3491
- # staked positions too (the simplified facet view reports 0 liquidity once the
3492
- # NFT is held by the gauge).
3493
- pos = _aero_read_position(w3, token_id)
3494
- if pos is None:
3495
- print(f" Cannot read position {token_id}.")
3496
- return
3497
- token0, token1, tick_lower, tick_upper, current_liq = pos
3498
- if current_liq == 0:
3499
- print(f" Position {token_id} has 0 liquidity (may already be closed).")
3500
- return
3501
-
3502
- sym0 = _resolve_token_symbol(w3, token0)
3503
- sym1 = _resolve_token_symbol(w3, token1)
3504
-
3505
- remove_liq = int(Decimal(str(current_liq)) * Decimal(str(percentage)) / Decimal(100))
3506
- if remove_liq <= 0:
3507
- print(f" Removal percentage {percentage}% yields 0 liquidity.")
3508
- return
3509
-
3510
- print(f"Position {token_id}: {sym0}/{sym1} ticks=[{tick_lower},{tick_upper}]")
3511
- print(f" Current liquidity: {current_liq}")
3512
- print(f" Removing: {remove_liq} ({percentage}%)")
3519
+ # Show what each position holds before closing (NPM.positions() is correct for
3520
+ # staked NFTs, which the simplified facet view reports as 0 liquidity).
3521
+ for tid in token_ids:
3522
+ pos = _aero_read_position(w3, tid)
3523
+ if pos is None:
3524
+ print(f" Position {tid}: cannot read (may not exist).")
3525
+ continue
3526
+ token0, token1, tick_lower, tick_upper, current_liq = pos
3527
+ sym0 = _resolve_token_symbol(w3, token0)
3528
+ sym1 = _resolve_token_symbol(w3, token1)
3529
+ print(f"Position {tid}: {sym0}/{sym1} ticks=[{tick_lower},{tick_upper}] "
3530
+ f"liquidity={current_liq}")
3513
3531
 
3514
- params = _aero_decrease_params(token_id, remove_liq, 0, 0)
3515
- # decreaseLiquidity on the facet (selector ca15558b)
3516
- dec_data = account.encode_abi("decreaseAerodromeLiquidity", args=[params])
3517
- dec_params_bytes = bytes.fromhex(dec_data[2:])[4:]
3518
- dec_calldata = AERODROME_SEL_DECREASE + dec_params_bytes
3532
+ # Encode batchRemoveStakedLiquidityAerodrome(uint256[] tokenIds) and append the
3533
+ # RedStone payload raw. Byte-for-byte layout (selector + uint256[] head + payload)
3534
+ # verified against the manual close 0x0d65...0a50.
3535
+ from eth_abi import encode as abi_encode
3536
+ encoded_ids = abi_encode(['uint256[]'], [token_ids])
3537
+ feeds = sorted(REDSTONE_AVAILABLE_FEEDS)
3538
+ payload = build_redstone_payload(feeds)
3539
+ close_calldata = AERODROME_SEL_BURN + encoded_ids + payload
3519
3540
 
3520
- # Pre-flight eth_call simulation — refuse to broadcast on revert. The decrease
3521
- # path is not RedStone-gated, so a bare eth_call is sufficient.
3541
+ # Pre-flight eth_call simulation — refuse to broadcast on revert. The close path
3542
+ # IS RedStone-gated, so the simulated calldata already carries the payload.
3522
3543
  try:
3523
3544
  w3.eth.call({"from": acct.address, "to": account.address,
3524
- "data": "0x" + dec_calldata.hex()})
3545
+ "data": "0x" + close_calldata.hex()})
3525
3546
  except Exception as e:
3526
3547
  print(f" Simulation reverted — aborting before broadcast: {type(e).__name__}: {str(e)[:200]}")
3527
3548
  return
@@ -3535,15 +3556,15 @@ def cmd_aero_remove_liquidity(token_id: int, percentage: float = 100.0,
3535
3556
  "from": acct.address,
3536
3557
  "to": account.address,
3537
3558
  "nonce": w3.eth.get_transaction_count(acct.address),
3538
- "gas": 4000000,
3559
+ "gas": 5000000,
3539
3560
  "chainId": CHAIN_ID,
3540
- "data": "0x" + dec_calldata.hex(),
3561
+ "data": "0x" + close_calldata.hex(),
3541
3562
  }
3542
- receipt = _sign_and_send(w3, acct, tx, "Remove liquidity", timeout=300, fallback_gas=4000000)
3563
+ receipt = _sign_and_send(w3, acct, tx, "Close Aerodrome position(s)", timeout=300, fallback_gas=5000000)
3543
3564
  ok = receipt["status"] == 1
3544
- if ok and percentage >= 100:
3545
- print(f" Position fully withdrawn. Collect fees then burn:")
3546
- print(f" degenprime aero-collect-fees --token-id {token_id} --execute")
3565
+ if ok:
3566
+ ids_str = ", ".join(str(t) for t in token_ids)
3567
+ print(f" Fully closed (unstaked + removed + collected + burned): {ids_str}")
3547
3568
 
3548
3569
 
3549
3570
  def cmd_aero_collect_fees(token_id: int, execute: bool = False):
@@ -3563,13 +3584,13 @@ def cmd_aero_collect_fees(token_id: int, execute: bool = False):
3563
3584
  account = w3.eth.contract(address=Web3.to_checksum_address(pa), abi=PRIME_ACCOUNT_ABI)
3564
3585
  print(f"Degen Account: {pa}")
3565
3586
 
3566
- # Read position composition for display
3567
- try:
3568
- pos = account.functions.getPositionCompositionSimplified(token_id).call()
3569
- except Exception as e:
3570
- print(f" Cannot read position {token_id}: {e}")
3587
+ # Read position for display via NPM.positions() — correct for staked NFTs (the
3588
+ # simplified facet view reports liquidity=0 once the gauge owns the NFT).
3589
+ pos = _aero_read_position(w3, token_id)
3590
+ if pos is None:
3591
+ print(f" Cannot read position {token_id}.")
3571
3592
  return
3572
- token0, token1, tick_data, liq = pos
3593
+ token0, token1, tick_lower, tick_upper, liq = pos
3573
3594
  sym0 = _resolve_token_symbol(w3, token0)
3574
3595
  sym1 = _resolve_token_symbol(w3, token1)
3575
3596
  print(f"Position {token_id}: {sym0}/{sym1} liquidity={liq}")
@@ -3830,16 +3851,17 @@ def _dispatch():
3830
3851
  return
3831
3852
  cmd_aero_add_liquidity(pool_key, amt0, amt1, slippage, execute, width)
3832
3853
  elif cmd == "aero-remove-liquidity":
3833
- token_id = None
3854
+ token_ids = []
3834
3855
  percentage = 100.0
3835
3856
  execute = "--execute" in args
3836
3857
  for i, a in enumerate(args):
3837
- if a == "--token-id" and i + 1 < len(args): token_id = int(args[i + 1])
3858
+ if a == "--token-id" and i + 1 < len(args): token_ids.append(int(args[i + 1]))
3838
3859
  if a == "--percentage" and i + 1 < len(args): percentage = float(args[i + 1])
3839
- if token_id is None:
3840
- print("Usage: degenprime aero-remove-liquidity --token-id N [--percentage 100] [--execute]")
3860
+ if not token_ids:
3861
+ print("Usage: degenprime aero-remove-liquidity --token-id N [--token-id M ...] [--execute]")
3862
+ print(" Fully closes (unstake + remove + collect + burn) each staked position. Full close only.")
3841
3863
  return
3842
- cmd_aero_remove_liquidity(token_id, percentage, execute)
3864
+ cmd_aero_remove_liquidity(token_ids, percentage, execute)
3843
3865
  elif cmd == "aero-collect-fees":
3844
3866
  token_id = None
3845
3867
  execute = "--execute" in args
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: primecli
3
- Version: 0.8.0
3
+ Version: 0.8.1
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
@@ -47,7 +47,7 @@ Built for agent use:
47
47
  - RedStone-signed solvency math handled internally, with a regression test pinning the half-boundary `toFixed(8)` encoding.
48
48
  - ParaSwap calldata validated client-side against the on-chain executor allowlist before broadcast.
49
49
 
50
- **Current version:** 0.8.0 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
50
+ **Current version:** 0.8.1 The 0.x line is pre-1.0, so breaking changes are possible. See [Releases](https://github.com/Mnemosyne-quest/primecli/releases).
51
51
 
52
52
  > **Breaking change in 0.5.0:** there is no longer a default signing key. Earlier versions silently fell back to a baked-in agent when no key was configured; that fallback has been removed. With no key configured, every command now fails closed with `No signing key found...`. Set a key explicitly (see [Configuration](#configuration)).
53
53
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "primecli"
7
- version = "0.8.0"
7
+ version = "0.8.1"
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