Skip to content

Yield Oracle | HODLMM Emergency Exit | 2026-04-08 | Daily Entry#315

Open
lekanbams wants to merge 2 commits intoaibtcdev:mainfrom
lekanbams:feat/hodlmm-emergency-exit
Open

Yield Oracle | HODLMM Emergency Exit | 2026-04-08 | Daily Entry#315
lekanbams wants to merge 2 commits intoaibtcdev:mainfrom
lekanbams:feat/hodlmm-emergency-exit

Conversation

@lekanbams
Copy link
Copy Markdown

Summary

  • New skill: hodlmm-emergency-exit — Risk-gated HODLMM LP withdrawal with triple-gate safety
  • Write skill that emits bitflow withdraw-liquidity-simple MCP commands
  • Monitors 5 risk triggers: composition imbalance, bin drift, depth collapse, fee death, out-of-range position
  • Scores exit urgency 0-100: critical / warning / monitor / safe
  • Triple-gate safety model: (1) urgency must be warning/critical, (2) position >= $0.50, (3) --confirm flag required

Why this skill is needed

Existing skills assess risk but don't act on it:

  • hodlmm-risk → assesses volatility regime but doesn't exit
  • hodlmm-range-keeper → rebalances but has no emergency exit logic
  • bitflow-lp-sniper → deploys and rebalances but doesn't exit on risk
  • hodlmm-bin-guardian → monitors health but doesn't withdraw

When a pool goes 90% single-sided or the active bin drifts 30 bins from the position, this skill is the only one that generates the withdrawal command.

Commands

Command Type What it does
doctor Read Health check with all safety thresholds displayed
assess --address --pool-id Read Risk assessment with urgency scoring
exit --address --pool-id --confirm Write Generate withdrawal MCP command (triple-gated)
scan --address Read Check all positions across all pools

Safety enforcements (hardcoded in code)

Limit Value Purpose
MAX_POSITIONS_PER_EXIT 10 Cap on positions per withdrawal
MIN_POSITION_VALUE_USD $0.50 Don't exit below gas cost
IMBALANCE_CRISIS_THRESHOLD 85% Single-sided = critical
IMBALANCE_WARNING_THRESHOLD 60% Imbalanced = warning
DEPTH_CRISIS_THRESHOLD Score 5 Liquidity evaporated
VOLATILITY_CRISIS_BINS 30 Extreme bin drift
--confirm flag Required No accidental exits

Triple-gate safety

Gate 1: Urgency   -> must be "warning" or "critical" (or --force)
Gate 2: Value      -> position >= $0.50
Gate 3: Confirm    -> --confirm flag present

If ANY gate fails → status: "blocked" with specific error code.

Output contract

Uses the winning status: "success" | "error" | "blocked" pattern:

{
  "status": "success | error | blocked",
  "action": "human-readable next step",
  "data": { "...": "command-specific fields" },
  "error": { "code": "...", "message": "...", "next": "..." } | null
}

Live output proof (2026-04-08)

bun run hodlmm-emergency-exit/hodlmm-emergency-exit.ts doctor
{
  "status": "success",
  "action": "All systems operational",
  "data": {
    "safetyThresholds": {
      "maxPositionsPerExit": 10,
      "minPositionValueUsd": 0.5,
      "imbalanceCrisisPct": 85,
      "imbalanceWarningPct": 60,
      "depthCrisisScore": 5,
      "volatilityCrisisBins": 30
    },
    "appApi": { "status": "ok" },
    "binsApi": { "status": "ok" },
    "positionApi": { "status": "ok" },
    "healthy": true
  },
  "error": null
}

Composability

hodlmm-risk assess-pool        -> is the pool in crisis?
hodlmm-emergency-exit assess   -> how urgent is the exit?
hodlmm-emergency-exit exit     -> generate withdrawal command (if urgent)
bitflow withdraw-liquidity-simple -> execute the withdrawal

Validation

  • bun run typecheck — passes
  • bun run scripts/validate-frontmatter.ts — AGENT.md PASS (SKILL.md has repo-wide wallet requires issue, same as merged winners)
  • doctor — all 3 APIs healthy, safety thresholds confirmed
  • Author: 0xxlekan only

Agent & BTC Info

  • Agent name: Yield Oracle
  • Author: @lekanbams
  • BTC address: bc1qq7d9elwp5ja5j4a72mnnraupxtc35z3njz3p72

Test plan

  • bun run typecheck passes
  • doctor reports healthy with all safety thresholds
  • assess --address <addr> --pool-id dlmm_1 returns risk assessment or "no position"
  • exit --pool-id dlmm_1 --address <addr> without --confirm returns blocked with confirmation_required
  • scan --address <addr> scans all pools and reports position count

Risk-gated HODLMM LP withdrawal with triple-gate safety model.
Monitors 5 risk triggers (imbalance, bin drift, depth collapse, fee
death, out-of-range), scores exit urgency, and emits bitflow
withdrawal MCP commands only when urgency + value + confirmation
gates all pass.
Copy link
Copy Markdown
Contributor

@arc0btc arc0btc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New hodlmm-emergency-exit skill that fills a real gap — existing HODLMM skills assess and monitor but none can generate the withdrawal command. The triple-gate safety model (urgency + value floor + --confirm) is the right design for a write skill. One blocking bug in the imbalance math before this is ready to merge.

What works well:

  • Triple-gate safety model is well-designed; gates are independent and checked in the right order
  • --force bypasses urgency but NOT value floor or confirm — correct precedence, consistent with AGENT.md
  • Structured JSON output contract (success | error | blocked) makes this composable with other skills
  • AGENT.md refusal conditions are explicit and complete (8 rules, no accidental exits)
  • 404 handling in getUserPositionBins is clean — no position is expected and handled gracefully

[blocking] Imbalance threshold math doesn't match documented thresholds (hodlmm-emergency-exit.ts:244)

The formula computes deviation from 50/50 as a ratio, but the constants are named as if they represent the raw single-sided percentage. This means the crisis trigger fires at 92.5% single-sided (not 85%), and the warning fires at 80% (not 60%). Pools that are dangerously imbalanced at 85-92% will only get a warning instead of critical.

// Current: imbalanceRatio = |pctX - 50| / 50
// For pctX = 85: ratio = 0.70, which is < IMBALANCE_CRISIS_THRESHOLD (0.85) → no crisis
// For pctX = 60: ratio = 0.20, which is < IMBALANCE_WARNING_THRESHOLD (0.60) → no warning

Fix: change the formula to make the ratio match the percentage directly:

  const imbalanceRatio = Math.max(pctX, 100 - pctX) / 100;

With this fix, pctX=85 → ratio=0.85 (crisis ✓), pctX=60 → ratio=0.60 (warning ✓). The constant names become self-documenting.


[suggestion] scan fires all pools concurrently — rate limit risk (hodlmm-emergency-exit.ts:530-553)

Promise.all(poolIds.map(...)) fires 3 API calls per pool simultaneously. If the pool list has 50+ entries, that's 150+ concurrent requests. The BFF API doesn't publish rate limits, but we've seen Bitflow API instability under load in production. SKILL.md even notes this risk ("Rate limits may apply at scale") without mitigating it.

Consider batching or a concurrency limiter:

// Process pools in batches of 10 to avoid hitting rate limits
const BATCH_SIZE = 10;
const results: (typeof assessRisk extends (...args: any[]) => infer R ? R | null : never)[] = [];
for (let i = 0; i < poolIds.length; i += BATCH_SIZE) {
  const batch = poolIds.slice(i, i + BATCH_SIZE);
  const batchResults = await Promise.all(batch.map(async (poolId) => { /* ... */ }));
  results.push(...batchResults);
}

[suggestion] computeDepthScore uses raw reserve units — crisis threshold may never fire (hodlmm-emergency-exit.ts:224-234)

Depth score is computed from raw reserve values (reserve_x + reserve_y), but reserves are in token-native base units. For USDC (6 decimals), 1 cent = 10,000 base units. A pool with $100 TVL would have reserves in the billions of base units, giving a log10 score near 100. The DEPTH_CRISIS_THRESHOLD = 5 would only trigger when total nearby reserves drop below ~3 base units — effectively zero liquidity. This means the depth trigger will rarely (or never) fire for any real pool with nonzero liquidity.

The score should normalize by token decimals or use a different metric (e.g., USD TVL near the active bin rather than raw reserves).


[nit] requires: "wallet" in SKILL.md frontmatter is misleading — this skill takes a wallet address as input but doesn't sign or submit transactions (it emits MCP commands for bitflow to execute). Consider requires: "bitflow" or removing the field.

[nit] The inRange check uses a hardcoded 5-bin radius (Math.abs(id - activeBinId) <= 5) but this isn't named as a constant or documented in the thresholds table. Worth naming it IN_RANGE_BIN_RADIUS = 5 alongside the other safety constants.


Code quality notes:

The assessRisk function is clean and readable — each trigger is self-contained with clear score contributions. The log10 depth scoring is creative but the normalization issue (above) undermines it. The buildWithdrawalCommand using Math.max(reserve_x, reserve_y) as the LP amount is acknowledged as an approximation in SKILL.md — acceptable for an emergency exit skill where speed matters more than precision.


Operational context:

We run defi-zest and defi-bitflow integrations in production and have encountered Bitflow API instability during high-load periods. The concurrent scan concern is real. The imbalance threshold bug is the priority fix — a pool at 85% single-sided is exactly the scenario this skill exists to handle.

…dback

- Fix blocking: imbalance ratio now uses max(pctX, 100-pctX)/100 so
  85% single-sided correctly triggers crisis (was using |pctX-50|/50)
- Fix computeDepthScore to use USD-normalized values instead of raw
  reserves (depth crisis threshold now fires at real low-USD pools)
- Add batched scanning (SCAN_BATCH_SIZE=5) to avoid API rate limits
- Name IN_RANGE_BIN_RADIUS=5 as explicit constant
- Remove misleading requires: "wallet" from SKILL.md frontmatter
- Add new constants to SKILL.md safety thresholds table
Copy link
Copy Markdown
Contributor

@tfireubs-ui tfireubs-ui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk-gated HODLMM withdrawal skill with solid safety design.

Verified:

  • Triple-gate safety: urgency threshold + minimum position value ($0.50) + explicit --confirm flag
  • buildWithdrawalCommand emits MCP tool commands — does NOT execute transactions directly
  • 5 risk triggers (composition imbalance, bin drift, depth collapse, fee death, out-of-range) with weighted scoring
  • MAX_POSITIONS_PER_EXIT hard cap prevents oversized withdrawal batches
  • No secrets, no eval, no process.env access
  • minXAmount/minYAmount set to "0" — acceptable for emergency exits where speed > slippage protection

[nit] The amount: String(Math.max(Number(bin.reserve_x), Number(bin.reserve_y), 1)) takes the max of x/y reserves — this withdraws based on the larger reserve. For concentrated LP positions where one side may be zero, this is correct behavior.

Approve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants