Skip to content

[simplex]: keep an order's evaluation for its confirmations plus 5 minutes, and redo it if it still expires, 0.18.3 - #1434

Merged
seunlanlege merged 2 commits into
mainfrom
fix/simplex-reevaluate-expired-evaluation
Oct 22, 2026
Merged

seunlanlege merged 2 commits into
mainfrom
fix/simplex-reevaluate-expired-evaluation

Conversation

@Wizdave97

@Wizdave97 Wizdave97 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

What this does

IntentFiller evaluates an order while it waits for the order's source confirmations, then queues its bids on the destination chain. Everything the evaluation cached (gas estimate, bid plans, filler outputs) expired 60 s after it was written. The bid loop's pin from #1424 keeps only what is still live when the loop starts.

When the confirmation wait outlasted that, the loop started with no plans and no gas estimate. Every bid then failed in prepareBidUserOp with No cached gas estimate found for order <id>, and the order was never evaluated again. Seen on a BSC order: evaluation complete at 18:58:00, confirmed on source at 18:59:18, bid failed at 18:59:19.

Cache lifetime

  • Every CacheService entry keyed by an order stores its own expiresAt, set when it is written. That covers the gas estimate, bid plans, filler outputs/inputs/budget, funding prepends, partial-fill and fee-check flags, matched limit orders, swap operations and pair classifications.
  • The default lifetime, CACHE_EXPIRY_MS, is now 300 s (was 60 s).
  • While an order waits for its source confirmations, handleNewOrder registers how long they take with CacheService.extendForConfirmations(orderId, waitMs).
    • The wait is the required confirmations times the source chain's block time, from ContractInteractionService.blockTimeSeconds (measured once at startup, now public).
    • Entries written in that time live the default plus the wait.
  • The wait is released once evaluation and confirmation are both done. Entries keep the expiry they were written with; later entries get the default.
  • With no block time to be had, the wait is 0 and the order keeps the default lifetime.
  • A pinned order's entries are judged as of the pin, as before.

Re-evaluation

handleNewOrder also hands executeOrder a re-evaluation of the order (evaluateOrder with the same canFill answers). Confirmations slower than the block time predicted, or a long chain-queue wait, can still outlast an evaluation. So when the queued task starts, it pins the order and checks the gas estimate: the first thing an evaluation caches, and what every bid signs.

  • If the gas estimate is gone, the order is evaluated again before any bid goes out, and the bids use the strategy and plans that evaluation chooses.
  • A re-evaluation that passes on the order sends no bid and reports why (orderSkipped). The expired evaluation is never used.
  • An evaluation still live when the task starts is used as is, so every bid on an order is priced by a single evaluation.

@hyperbridge/simplex and simplex-desktop are bumped to 0.18.3. simplex-v0.18.3 is tagged on this branch's head (13ad1a41d). Note: docs/ai/changelog/2026-10-22-order-cache-lives-through-its-confirmations.md.

Validation

  • End-to-end through handleNewOrder (bid-loop-cache-expiry.test.ts), with two confirmations at a 100 s block time:
    • Confirmations that take longer than the default lifetime but within the default plus the wait keep the first evaluation, and the bid goes out. With the wait forced to 0 this test fails (two evaluations).
    • Confirmations that take longer than that are evaluated again and bid on. On main this order is evaluated once and gets no bid.
  • CacheService lifetime:
    • An entry written while the order waits lives the default plus the wait, and releasing the wait does not shorten it.
    • An entry written after the release, or for another order, lives the default.
  • Bid loop:
    • Re-evaluated bids go out at the new prices even when each submission also outlasts the cache.
    • A re-evaluation that passes sends nothing.
    • A live evaluation is not re-evaluated.
  • source-reorg.test.ts stubs the two new contract-service members; limit-order-budget.test.ts reads the expiry instead of hardcoding 61 s.
  • pnpm --filter @hyperbridge/simplex test:unit: 300 passed. test:data: 139 passed. test:filler: 75 passed, 5 skipped. src/tests/core + src/tests/services: 571 passed, 3 skipped.
  • lint: no errors. tsc: nothing reported in the touched files.

🤖 Generated with Claude Code

… its bids

The bid loop's pin keeps only what is still live when the loop starts, and
a confirmation or chain-queue wait longer than CACHE_EXPIRY_MS left it with
no plans and no gas estimate, so every bid failed with "No cached gas
estimate" and the order was never evaluated again. The queued task now
checks the gas estimate after pinning and, when it is gone, evaluates the
order again before any bid goes out.
@seunlanlege

Copy link
Copy Markdown
Member

The issue here is that the order from base takes some time for it to reach required confirmations, can't we cache the evaluation for the duration of the confirmation period?

@seunlanlege seunlanlege left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyways bump simplex

@Wizdave97

Copy link
Copy Markdown
Member Author

The issue here is that the order from base takes some time for it to reach required confirmations, can't we cache the evaluation for the duration of the confirmation period?

Yeah, I can change the default expiry to use the blocks + the 60s.

…nutes, 0.18.3

Every order-keyed CacheService entry now stores its own expiresAt. The
default lifetime is 300 s, and while an order waits for its source
confirmations, handleNewOrder registers how long they take (required
confirmations times the source block time measured at startup), so what
the evaluation caches lives the default plus that wait. The wait is
released once evaluation and confirmation are both done; entries keep the
expiry they were written with.

Bumps @hyperbridge/simplex and simplex-desktop to 0.18.3.
@Wizdave97 Wizdave97 changed the title [simplex]: evaluate an order again when its evaluation expired before its bids [simplex]: keep an order's evaluation for its confirmations plus 5 minutes, and redo it if it still expires, 0.18.3 Oct 22, 2026
@seunlanlege
seunlanlege merged commit d6a0de6 into main Oct 22, 2026
16 of 17 checks passed
@seunlanlege
seunlanlege deleted the fix/simplex-reevaluate-expired-evaluation branch October 22, 2026 20:07
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