improve(hyperliquid): enrich excessive-slippage warn with pair + blocked SwapFlows#3439
improve(hyperliquid): enrich excessive-slippage warn with pair + blocked SwapFlows#3439droplet-rl wants to merge 2 commits into
Conversation
…ked SwapFlows The "Not submitting new limit order due to excessive slippage" warning fired from HyperliquidExecutor#updateOrderAmount currently only carries bestAsk + maxSlippage, with no indication of which pair is affected, which SwapHandler is queueing, or which user-level SwapFlows are stuck behind the rejection. That makes any "my deposit isn't moving" question into a manual cross-reference against open orders and chain state. Extend the log with: - pair / baseToken / finalToken / swapHandler — so the affected pair and underlying contract are obvious without grepping config. - orderSize — the would-be order size, in baseToken human units. - bpsFromParity — the actual slippage we saw, so it's clear how far over the limit we are (not just that we were over). - blockedSwapFlows — quoteNonce, finalRecipient, evmAmountIn for each outstanding (un-finalized) SwapFlowInitialized on the pair. This is the "which user is affected" data the warning is silent about today. Also fix the existing maxSlippage field, which read the global maxSlippageBps even when a route-specific override was in effect — log now reflects the threshold actually applied. The extra getOutstandingOrdersOnPair fetch is in the warn branch only, so the hot path is unchanged when slippage is within tolerance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac9d8e43db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const sizeFormatter = createFormatFunction(2, 4, false, pair.baseTokenDecimals); | ||
| // Fetch the user-level SwapFlows blocked by this rejection so the log identifies | ||
| // which deposits are held up. Only done in this warning branch to keep the hot path cheap. | ||
| const blockedSwapFlows = await this.getOutstandingOrdersOnPair(pair); |
There was a problem hiding this comment.
Query current blocks for blocked-flow logging
When this warning fires in the long-running executor after startup, calling getOutstandingOrdersOnPair(pair) without a to value uses the dstSearchConfig.to block captured during initialize(), so any SwapFlowInitialized events observed after that startup block are excluded from blockedSwapFlows. This means the new log often omits the exact deposits that triggered the slippage rejection; pass a live/current block (or allow the query to use latest) for this logging path.
Useful? React with 👍 / 👎.
| const sizeFormatter = createFormatFunction(2, 4, false, pair.baseTokenDecimals); | ||
| // Fetch the user-level SwapFlows blocked by this rejection so the log identifies | ||
| // which deposits are held up. Only done in this warning branch to keep the hot path cheap. | ||
| const blockedSwapFlows = await this.getOutstandingOrdersOnPair(pair); |
There was a problem hiding this comment.
Keep slippage rejection independent of log enrichment
When the market is outside the configured slippage bound, this new event-query is only for enriching the warning, but any dst RPC/pagination failure here now rejects updateOrderAmount before it can return the non-actionable result. Since processTasks awaits task promises without a catch, a transient failure in this observability-only query can stop the executor in exactly the backoff path where it previously just logged and continued; wrap this enrichment so failures fall back to an empty/partial blockedSwapFlows log.
Useful? React with 👍 / 👎.
… enrichment Address Codex review feedback on #3439: - Query getOutstandingOrdersOnPair with the current dst block instead of the dstSearchConfig.to captured at initialize(), so SwapFlows initialized after startup are not omitted from the warning. - Wrap the enrichment in try/catch and fall back to an empty list so a transient dst RPC/pagination failure cannot reject updateOrderAmount and stop processTasks.
|
@codex addressed both comments in 2e8e18d:
|
Follow-up ResultI reviewed the current PR head (
No code changes were needed, so I did not create a commit or open a new PR. Testing / Checks
|
|
Thanks for the confirmation — no further action needed on my end. |
Summary
Enriches the
Not submitting new limit order due to excessive slippagewarning emitted byHyperliquidExecutor#updateOrderAmountso a single log line is enough to identify which pair, which SwapHandler, and which user-level SwapFlows are being held up by the rejection. Today the warning only carriesbestAskandmaxSlippage— for example, see this prod fire: https://risklabs.slack.com/archives/C0A15N2Q755/p1780111530258689Before
To answer "whose deposits are stuck?" we had to grep config for the pair, look up the SwapHandler on-chain, and re-query SwapFlowInitialized events ourselves.
After
The warn now includes:
pair,baseToken,finalToken,swapHandler— affected pair + holding contract, no config grep needed.orderSize— would-be order size in baseToken human units.bpsFromParity— actual slippage observed, so it's clear how far over the threshold we are.maxSlippage— now correctly reflects the route-specific override (maxSlippageByRoute[pair.name] ?? maxSlippageBps); previously always logged the global default even when an override was active.blockedSwapFlows— array of{ quoteNonce, finalRecipient, evmAmountIn }for each outstanding (un-finalized)SwapFlowInitializedon the pair. This is the "which user is being held up" signal the warning is silent about today.Cost
getOutstandingOrdersOnPair(pair)does twopaginatedEventQuerycalls against HyperEVM RPC. It runs only inside the warn branch (slippage actually exceeded), not on every block in steady state. Net cost in normal operation: zero.Test plan
yarn typecheck— clean locally.yarn prettier --check src/hyperliquid/HyperliquidExecutor.ts— clean.pair,swapHandler,blockedSwapFlows, etc. Should fire on the same conditions as before (no behavioral change).Related
Came out of the broader investigation in https://app.slack.com/client/T90K0AL22/C0AQEHGS56Z/thread/C0AQEHGS56Z-1780148503.175839 (stuck OFT/HyperCore deposit). Independent of #3438 (lookback default bump) — open as a separate PR so each can land on its own merits.
🤖 Generated with Claude Code