Skip to content

feat(clmm): orphaned-position lifecycle, bin_count passthrough, Raydium via Gateway - #217

Open
fengtality wants to merge 3 commits into
mainfrom
feat/lp-close-retry-ownership
Open

feat(clmm): orphaned-position lifecycle, bin_count passthrough, Raydium via Gateway#217
fengtality wants to merge 3 commits into
mainfrom
feat/lp-close-retry-ownership

Conversation

@fengtality

@fengtality fengtality commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

API-side of the gateway#678 retry-ownership work, plus the CLMM pool-info plumbing that goes with it. Canonical design: docs/retry-architecture.md (in the gateway PR), esp. the two-topology section: executors created via the API run in-process with no controller, so the API owns the "react to a stranded position" role.

Orphaned-position lifecycle

  • DB-aware stop: stop on a terminal executor returns already_terminated with close_type, position_address, orphaned_position, and hold_reason instead of the 404 dead end #678 hit (terminal executors are popped from memory within one tick, so "not in memory" almost always means "already terminated"). 404 is reserved for ids the DB has never seen.
  • Orphan flagging on completion: an involuntary hold (POSITION_HOLD with hold_reason — an LP close that exhausted its retries) or a legacy FAILED-with-position gets orphaned_position: true persisted plus an error-level log. Voluntary holds never match (a successful close clears position_address first).
  • GET /executors/positions/orphaned: recovery candidates, SQL-filtered to lp_executor — involuntary holds, FAILED-with-position, and SYSTEM_CLEANUP restarts (flagged needs_onchain_reconciliation).
  • POST /executors/{id}/resolve-orphan: mark recovered after the position is closed externally; silences listings and agent warnings.
  • bots/controllers lp_rebalancer mirror: halt + skip accounting for executors that ended with a live position.

CLMM pool-info: bin_count, and Raydium consistency

  • GET /gateway/clmm/pool-info accepts bin_count and forwards it to Gateway's unified trading/clmm/pool-info, making the per-tick liquidity distribution reachable for orca, raydium, uniswap and pancakeswap (Meteora always returns its own bins). The response model already carried bins.
  • Raydium no longer bypasses Gateway. The route special-cased it: skipping Gateway entirely, calling api-v3.raydium.io directly, and reshaping that response to imitate Gateway's. That divergence cost real data — the transform hardcoded active_bin_id to None, bin_step to 1, and bins to [] — and it meant Raydium could not answer bin_count at all, since only Gateway computes the tick distribution. Raydium now takes the same path as every other CLMM connector; the Raydium API helpers and their aiohttp import go with it.

Companion PRs

Note

routers/gateway_clmm.py also gains some unrelated line-wrapping and a dropped unused import: the flake8 pre-commit hook lints the whole file and would not otherwise accept a commit touching it.

Validation

36 gateway-client contract tests pass. Validated live on mainnet with the companion branches deployed:

  • Forced close-failure cascade terminated as the involuntary hold, surfaced in the orphan listing with hold_reason, re-stop returned already_terminated, and resolve-orphan cleared it after a direct gateway close recovered all funds + rent.
  • bin_count=5 returns populated bins for orca and raydium through this endpoint; bin_count=0 returns none. Raydium via Gateway now reports a real active_bin_id where the bypass reported None.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj

… listing, DB-aware stop, resolve

API-side of the gateway#678 retry-ownership work (canonical design:
docs/retry-architecture.md in the companion gateway PR). Executors created
via the API run in-process with no controller, so the API owns the
"react to a stranded position" role:

- Stop on a terminal executor returns already_terminated with close_type,
  position_address, orphaned_position, and hold_reason instead of the 404
  dead end #678 hit (terminal executors are popped from memory within one
  tick, so "not in memory" almost always means "already terminated").
  404 is reserved for ids the DB has never seen.
- Completion flags stranded exposure in the persisted final state:
  an involuntary hold (POSITION_HOLD with hold_reason set — an LP close
  that exhausted its retries) or a legacy FAILED-with-position gets
  orphaned_position: true and an error-level log. Voluntary holds never
  match (a successful close clears position_address first).
- GET /executors/positions/orphaned lists recovery candidates
  (SQL-filtered to lp_executor; involuntary holds, FAILED-with-position,
  and SYSTEM_CLEANUP restarts flagged needs_onchain_reconciliation).
- POST /executors/{id}/resolve-orphan marks a candidate recovered after
  the position is closed externally, silencing listings and warnings.
- bots/controllers lp_rebalancer mirror: halt + skip accounting for
  executors that ended with a live position (re-creating one would mint a
  second position on top of the stranded one).

Validated live on mainnet: forced close-failure cascade terminated as the
involuntary hold, surfaced in the orphan listing with hold_reason,
re-stop returned already_terminated, and resolve-orphan cleared it after
a direct gateway close recovered all funds + rent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HahKfEY9rvKnZijrzUAFSq
@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a durable API-side lifecycle for LP executors that terminate with potentially stranded on-chain positions.

  • Flags qualifying terminal executor states and persists their recovery metadata.
  • Adds database-backed orphan listing and resolution endpoints.
  • Makes repeated stop requests return terminal executor details from the database.
  • Halts the mirrored LP rebalancer while its current process knows of an unresolved position.
  • Routes CLMM pool information through Gateway with optional bin-count forwarding.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up scope.

No blocking failure remains.

Important Files Changed

Filename Overview
bots/controllers/generic/lp_rebalancer/lp_rebalancer.py Adds a process-local halt when a newly terminated LP executor still reports an on-chain position and avoids accounting its pool balances as returned funds.
database/repositories/executor_repository.py Adds an SQL-filtered query for recent executor records matching selected close types and an optional executor type.
models/executors.py Extends stop responses with terminal-state metadata and defines orphan-listing response models.
routers/executors.py Exposes endpoints for listing and resolving database-backed orphan candidates.
services/executor_service.py Implements DB-aware stop behavior, orphan persistence and discovery, and explicit resolution of recovered positions.
routers/gateway_clmm.py Replaces the direct Raydium pool lookup with the common Gateway path and forwards an optional bin count.
services/gateway_client.py Adds optional bin-count forwarding to Gateway CLMM pool-information requests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[LP executor running] --> B{Terminal outcome}
    B -->|Normal close| C[Persist terminal state]
    B -->|Involuntary hold or failed with position| D[Flag orphan in final state]
    B -->|Restart cleanup| E[Mark for on-chain reconciliation]
    D --> F[Orphan listing]
    E --> F
    F --> G[Operator reconciles position externally]
    G --> H[Resolve orphan in database]
    H --> I[Remove from orphan listing]
    D --> J[Process-local controller halt]
    J --> K[Explicit controller restart after recovery]
Loading

Reviews (3): Last reviewed commit: "refactor(clmm): route Raydium pool-info ..." | Re-trigger Greptile

Comment thread bots/controllers/generic/lp_rebalancer/lp_rebalancer.py
Comment thread bots/controllers/generic/lp_rebalancer/lp_rebalancer.py
fengtality and others added 2 commits August 13, 2026 09:57
GET /gateway/clmm/pool-info accepts bin_count and forwards it to Gateway's
unified trading/clmm/pool-info, so the per-tick liquidity distribution is
reachable for orca, raydium, uniswap and pancakeswap (Meteora always returns
its own bins). The response model already carried bins.

Requests with bin_count > 0 skip the direct-Raydium-API shortcut: that API
returns no bin distribution, and only Gateway computes it from on-chain ticks.

Also wraps pre-existing long lines and drops an unused import in
routers/gateway_clmm.py — the flake8 pre-commit hook lints the whole file and
would not otherwise accept a commit touching it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…her connector

/gateway/clmm/pool-info special-cased Raydium: it skipped Gateway entirely,
called api-v3.raydium.io directly, and reshaped that response to look like
Gateway's. That divergence cost real data — the transform hardcoded
active_bin_id to None, bin_step to 1, and bins to [] — and it meant Raydium
could not answer bin_count at all, since only Gateway computes the tick
distribution.

Raydium now takes the same path as meteora/orca/uniswap/pancakeswap. The
Raydium API helpers and their aiohttp import go with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fengtality fengtality changed the title feat(executors): orphaned-position lifecycle for LP executors — flag, listing, DB-aware stop, resolve feat(clmm): orphaned-position lifecycle, bin_count passthrough, Raydium via Gateway Aug 13, 2026
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.

1 participant