Skip to content

Stage - #100

Merged
k0beLeenders merged 14 commits into
mainfrom
stage
Jul 24, 2026
Merged

Stage#100
k0beLeenders merged 14 commits into
mainfrom
stage

Conversation

@k0beLeenders

Copy link
Copy Markdown
Contributor

No description provided.

k0beLeenders and others added 14 commits June 19, 2026 17:18
Targeted LUTs are only created for oversized ("heavy") liquidation txs;
regular ones fit within the predefined group LUTs. But the single shared
`GroupedLuts.targeted` slot was reused across liquidations, which forces a
shared-state dance on deactivate and makes concurrent liquidations
impossible (two would collide on the one slot).

Make each targeted LUT self-contained: `get_targeted_lut` creates a fresh
LUT and returns it; the caller owns the key and deactivates exactly that
key via `deactivate_lut(key)`. No shared slot, so LUT create/deactivate is
inherently concurrency-safe. Prerequisite for parallel execution (1b).

Behaviour otherwise unchanged: heavy-tx detection, group-LUT selection,
and the deactivated-LUT close-after-cooldown sweep are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Liquidations ran strictly one-at-a-time, waiting for each to confirm
before starting the next — slow to react when many accounts become
liquidatable at once (e.g. a price crash) and easy for competing bots to
beat.

Run the ranked intents through a bounded worker pool (thread::scope, cap
MAX_CONCURRENT_LIQUIDATIONS = 8). Workers pull the next-highest-profit
intent from a shared queue, so highest-profit-first ordering is kept.

Safe now that (1a) targeted LUTs are per-liquidation: the executor's
shared state is Mutex/Atomic-guarded, RpcClient/JitoClient/Keypair are
Send+Sync, and the strategy's tokio runtime is switched to multi-thread
so concurrent DEX-quote block_on calls don't collide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A marginfi-account geyser update was queued for the liquidatability check
unconditionally. A debt-free account can't be liquidated, so evaluating it
is wasted work (it early-returns in process_account anyway).

try_insert now returns whether the account carries any liability; the
geyser handler only queues it when it does. A debt-free account rejoins
the queue the instant it borrows — that update reports liabilities.

The has-liabilities check now reads marginfi's balance-derived indexer
flags (is_lending_only / is_empty) instead of scanning balances. They're
synced on every balance-mutating instruction, so they're accurate the
moment an account borrows/repays, and a borrower is never wrongly flagged
lending-only (so we can't skip a liquidatable account). Also drives the
existing bank/oracle fan-out filter (accounts_without_liabilities).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cargo-chef base was pinned to rust 1.85, but `cargo chef cook` runs
before rust-toolchain.toml is copied, so the image's 1.85 was used — and
current Solana/tonic deps require rustc >= 1.88/1.89. Bump the base to
latest-rust-1.90, matching rust-toolchain.toml (1.90.0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
marginfi 0.1.9 requires EXACTLY 4 oracle accounts for StakedWithPythPush
banks — get_remaining_accounts_per_bank returns 5 (bank + 4 oracles) and
try_from_bank asserts ais.len() == 4 in every transition mode — so both the
rebalancer's pricing and liquidations were failing with 6051
(WrongNumberOfOracleAccounts) because eva01 only ever passed 3 oracles.

The 4th account is the on-ramp. It's only key-checked and read on-chain in
OnRampEnabled mode; in PreTransition (current) the program ignores ais[3]
entirely but still asserts the count. So we always emit a 4th account:
the derived on-ramp when available (correct once OnRampEnabled), otherwise a
duplicate of sol_pool purely to satisfy the count.

- utils::staked_onramp mirrors the program's expected_staked_onramp:
  oracle_keys[3] when set, else the on-ramp PDA derived from the validator
  vote account (integration_acc_1); None when neither is populated.
- banks: include the on-ramp in get_oracles() so it's loaded/streamed;
  expose get_staked_onramps().
- cache_loader: insert an empty placeholder for derivable on-ramps that don't
  exist on-chain yet (PreTransition); Geyser streams the real account over it
  once the pool becomes OnRampEnabled.
- oracle: OracleWrapper pricing always passes 4 account infos, falling back
  to a sol_pool copy for the on-ramp slot when it isn't cached.
- marginfi_account: staked observation list always emits 5 accounts (bank +
  4 oracles), duplicating sol_pool when the on-ramp isn't derivable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@k0beLeenders
k0beLeenders requested a review from IliaZyrin as a code owner July 24, 2026 07:10
@k0beLeenders
k0beLeenders merged commit 1a65a86 into main Jul 24, 2026
3 of 4 checks passed
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.

2 participants