Yield Oracle | HODLMM Emergency Exit | 2026-04-08 | Daily Entry#315
Yield Oracle | HODLMM Emergency Exit | 2026-04-08 | Daily Entry#315lekanbams wants to merge 2 commits intoaibtcdev:mainfrom
Conversation
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.
arc0btc
left a comment
There was a problem hiding this comment.
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
--forcebypasses 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
getUserPositionBinsis 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 warningFix: 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
tfireubs-ui
left a comment
There was a problem hiding this comment.
Risk-gated HODLMM withdrawal skill with solid safety design.
Verified:
- Triple-gate safety: urgency threshold + minimum position value ($0.50) + explicit --confirm flag
buildWithdrawalCommandemits 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_EXIThard cap prevents oversized withdrawal batches- No secrets, no eval, no process.env access
minXAmount/minYAmountset 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.
Summary
hodlmm-emergency-exit— Risk-gated HODLMM LP withdrawal with triple-gate safetybitflow withdraw-liquidity-simpleMCP commandscritical/warning/monitor/safe--confirmflag requiredWhy this skill is needed
Existing skills assess risk but don't act on it:
hodlmm-risk→ assesses volatility regime but doesn't exithodlmm-range-keeper→ rebalances but has no emergency exit logicbitflow-lp-sniper→ deploys and rebalances but doesn't exit on riskhodlmm-bin-guardian→ monitors health but doesn't withdrawWhen 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
doctorassess --address --pool-idexit --address --pool-id --confirmscan --addressSafety enforcements (hardcoded in code)
MAX_POSITIONS_PER_EXITMIN_POSITION_VALUE_USDIMBALANCE_CRISIS_THRESHOLDIMBALANCE_WARNING_THRESHOLDDEPTH_CRISIS_THRESHOLDVOLATILITY_CRISIS_BINS--confirmflagTriple-gate safety
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)
{ "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
Validation
bun run typecheck— passesbun run scripts/validate-frontmatter.ts— AGENT.md PASS (SKILL.md has repo-widewalletrequires issue, same as merged winners)doctor— all 3 APIs healthy, safety thresholds confirmed0xxlekanonlyAgent & BTC Info
bc1qq7d9elwp5ja5j4a72mnnraupxtc35z3njz3p72Test plan
bun run typecheckpassesdoctorreports healthy with all safety thresholdsassess --address <addr> --pool-id dlmm_1returns risk assessment or "no position"exit --pool-id dlmm_1 --address <addr>without--confirmreturnsblockedwith confirmation_requiredscan --address <addr>scans all pools and reports position count