Stage - #100
Merged
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.