Claude
Skills
Sign in
Back

hodlmm-inventory-balancer

Included with Lifetime
$97 forever

Detects HODLMM LP inventory drift (token-ratio imbalance from one-sided swap flow) and restores the target ratio via a corrective Bitflow swap plus a hodlmm-move-liquidity redeploy, gated by the 4h per-pool cooldown.

General

What this skill does


# HODLMM Inventory Balancer

## What it does

Detects **inventory drift** — the silent token-ratio imbalance that builds up in a HODLMM LP position when swap flow repeatedly drains one side of the pair even while the active bin holds its price. Computes a **price-weighted** exposure ratio across every user bin (price × liquidity share, not raw token counts, handling bins below/at/above the active bin correctly), compares it to an operator-configured target (default 50:50), and when the absolute deviation exceeds `--min-drift-pct` (default 5%) executes a corrective swap via the Bitflow SDK and a redeploy via `hodlmm-move-liquidity run --confirm`.

## Why agents need it

Without this skill an agent that wanted symmetric sBTC/STX exposure ends up directionally pulled by swap flow — 70/30 instead of 50/50 — while still appearing "in range." Symmetric-exposure management is what separates a real market maker from a passive directional position-taker. This skill closes the gap `hodlmm-move-liquidity` doesn't: it fixes *inventory* drift where move-liquidity only fixes *price* drift.

## Safety notes

- **Writes to chain.** Executes a Bitflow swap and (unless `--skip-redeploy`) an atomic bin-to-bin redeploy via `hodlmm-move-liquidity` CLI (which itself calls the DLMM router's multi-move family). Mainnet only. Proven end-to-end on `dlmm_1` across 2 full cycles: swap `cd71c8a5…` + redeploy `0349cbb0…`, then swap `134df5e1…` + redeploy `9cbe5903…`.
- **`run` requires `--confirm=BALANCE`.** Without it, the command exits with the computed plan in dry-run form.
- **JingSwap explicitly excluded in v1** — unaudited. Only pools whose pair is tradeable via Bitflow are eligible.
- **Min drift threshold** `--min-drift-pct` default 5%. Below that, no-op. Avoids thrashing on noise.
- **Max correction size** `--max-correction-sats` caps a single balancing swap. Prevents an outsized correction during extreme flow events.
- **Bitflow quote staleness gate** `--max-quote-staleness-seconds` default 45s (one full 15–19s pipeline cycle of margin on top of freshness floor).
- **Explicit slippage** — every corrective swap sends `minimum-output` computed from a slippage budget. Default 0.5%, overridable via env var `INVENTORY_BALANCER_SLIPPAGE_BPS` (integer bps) or `--slippage-bps` flag.
- **4-hour per-pool cooldown gate.** The skill reads `~/.hodlmm-move-liquidity-state.json` and refuses to start a cycle that would have the redeploy step blocked (unless `--skip-redeploy` is passed, in which case the swap-only correction still writes a state marker for later redeploy resumption).
- **Meta-cooldown** 1 hour across the balancer itself to prevent re-correcting inside the same swap-flow event.
- **Post-conditions**: `PostConditionMode.Allow` with a **dual-pin envelope** on every chain-writing leg. Each leg is sided per-token (skip the asset whose total is 0) and routes STX vs FT through `resolveTokenAsset()` so the two codepaths cannot drift apart.
  - **Corrective swap (default + 3-leg leg 2)** — mirrors the canonical `swap-simple-multi` pattern: `Pc.principal(sender).willSendLte(amount_in)` on the input token + `Pc.principal(pool.pool_contract).willSendGte(min_out)` on the output token. `min_out` is the same value passed to the router's `min-received` uint argument (`ERR_MINIMUM_RECEIVED` fires internally if undersold). Live proof tx [`0xf4f49328…`](https://explorer.hiro.so/txid/0xf4f4932800a80234845a8d199556ad9c0ff4aa99874a95c819c13779b164cbc8?chain=mainnet): `post_condition_mode: allow`, 2 post-conditions (sender `lte 6,468 sbtc-token` + pool `gte 4,993,915 usdcx-token`), 2 `fungible_token_asset` events, `tx_status: success`.
  - **3-leg withdraw-slice (leg 1)** — three-pin envelope: `Pc.principal(sender).willSendLte(total_shares).ft(pool.pool_contract, 'pool-token')` (DLP burn cap) + `Pc.principal(pool.pool_contract).willSendGte(total_min_x_raw)` + `…willSendGte(total_min_y_raw)` (X/Y receive floors). Asset name `pool-token` verified live on tx [`0x89315a8b…`](https://explorer.hiro.so/txid/0x89315a8b935b3e4db32ad753b77af4bf853f28dc5b04ca6aa25d7cca9fc1cf8a?chain=mainnet) burn event; uniform across all DLMM pools (single template deployer). These pins ride alongside the contract-level aggregate `min-x-amount-total` / `min-y-amount-total` floors and per-bin `min-x` / `min-y` on each position tuple — four-layer safety envelope on a single tx.
  - **3-leg redeposit (leg 3)** — wallet-level send caps on the sender: `Pc.principal(sender).willSendLte(total_x_raw × 1.05)` + `…willSendLte(total_y_raw × 1.05)`. The 5% headroom matches the per-tuple `max-(x|y)-liquidity-fee` ceiling in the function args.
  - Allow mode (vs Deny + per-fee enumeration) preserved because per-bin `bin-liquidity-fee` accruals and protocol fees route inside `dlmm-core`'s `unclaimed-protocol-fees` map / bin balances and don't always emit FT transfer events.
  - Default-mode redeploy (the `hodlmm-move-liquidity` CLI invocation) inherits its own contract-level slippage (`max-liquidity-fee` ≤ 5%; `min-dlp` handled upstream with bin-price-aware semantics).
- **Wallet-balance precondition**: the corrective swap transfers the over-weight token **from the sender's wallet**, so the operator must hold a free balance of that token. If all of the over-weight side is locked inside LP bins, the agent either tops up externally or withdraws a slice from the position first (outside this skill's v1 scope).
- **Refusal conditions** (enumerated in AGENT.md): pool volume too thin for corrective swap, Bitflow quote staleness exceeds gate, previous-cycle state marker unresolved, wallet gas reserve below floor, wallet balance of input token below required amount, move-liquidity cooldown active and `--skip-redeploy` not passed.

## Commands

### install-packs

Installs the Stacks SDK packages the executor needs. Idempotent.

```bash
bun run hodlmm-inventory-balancer/hodlmm-inventory-balancer.ts install-packs
```

### doctor

Pre-flight: wallet readable, Bitflow App + Quotes APIs reachable, at least one HODLMM pool with a user position, move-liquidity cooldown status surfaced as minutes remaining, prior state-marker inspected for unresolved cycles, wallet STX gas reserve sufficient.

```bash
bun run hodlmm-inventory-balancer/hodlmm-inventory-balancer.ts doctor
```

### status

Read-only. Per eligible pool: current effective token ratio, target ratio, absolute deviation, active bin, cooldown minutes remaining, last cycle outcome from the state marker.

```bash
bun run hodlmm-inventory-balancer/hodlmm-inventory-balancer.ts status [--pool <id>]
```

### recommend

Dry-run of the full cycle: computes the corrective swap plan (direction, `amount_in`, `minimum_out`) and the redeploy plan (via `hodlmm-move-liquidity` CLI `--dry-run`). Prints JSON without broadcasting. Useful as a pre-check before `run`.

```bash
bun run hodlmm-inventory-balancer/hodlmm-inventory-balancer.ts recommend [--pool <id>] [--target-ratio 50:50] [--min-drift-pct 5]
```

### run

Core execution. Requires `--confirm=BALANCE` (the word `BALANCE`, not just any value). Without it, behaves like `recommend`. Full cycle: cooldown check → corrective swap → state marker → redeploy → state marker cleared. If `--skip-redeploy` is passed, executes the swap only and leaves a `swap_done_redeploy_pending` marker so a later run picks up from the redeploy step without re-swapping.

```bash
bun run hodlmm-inventory-balancer/hodlmm-inventory-balancer.ts run \
  --confirm=BALANCE \
  [--pool <id>] \
  [--target-ratio 50:50] \
  [--min-drift-pct 5] \
  [--max-correction-sats 500000] \
  [--max-quote-staleness-seconds 45] \
  [--slippage-bps 50] \
  [--skip-redeploy] \
  [--force-direction "X->Y" | "Y->X"] \
  [--force-amount-in-raw <n>] \
  [--allow-rebalance-withdraw] \
  [--max-slice-bps <0..8000>]
```

Wallet password is read from the `WALLET_PASSWORD` env var. There is no `--password` CLI flag by design — an argv entry would surface in `/proc/<pid>/cmdline` and `ps auxww` for the process 
Files: 3
Size: 121.9 KB
Complexity: 41/100
Category: General

Related in General