Skip to content

feat(swapper): per-swapper quote deadlines, honored by the api quote store - #12520

Merged
kaladinlight merged 17 commits into
developfrom
feat/swapper-quote-deadlines
Aug 7, 2026
Merged

feat(swapper): per-swapper quote deadlines, honored by the api quote store#12520
kaladinlight merged 17 commits into
developfrom
feat/swapper-quote-deadlines

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Aug 3, 2026

Copy link
Copy Markdown
Member

Description

Quotes now carry the deadline they are actually safe to execute against, and the API honors it end to end. Previously the quote response advertised a fixed expiresAt of now+60s regardless of swapper, and the quote store held quotes for a flat 15 minutes — neither reflected protocol reality, and for deposit-address swappers a stale quote is a funds-safety issue (THORChain/MAYAChain inbound addresses rotate; NEAR Intents deposit addresses deactivate; Chainflip channels expire).

TradeQuote.deadline (epoch ms, required): every swapper populates it, provider expiry first:

  • THORChain/MAYAChain — the daemon quote expiry (unix s), which scopes the quoted inbound address ("Do not send funds after the expiry"); longtail→L1 additionally min'd with the aggregator swapIn deadline (10 min pinned at quote time)
  • NEAR Intents — the requested refund deadline (30 min, 60 min for BTC pairs): deposits must be mined before it to execute as quoted; the provider's response deadline (~72h, when the deposit address goes fully inactive) is only used if it is somehow tighter
  • Chainflip — flat 6h: sourceExpiryBlock is a source-chain height we can't cheaply convert to time, and channels live ~24h, so 6h keeps a 4× margin
  • CoW validTo, Bebop expiry (EVM + Solana sealed tx), Across quoteExpiryTimestamp, Ston.fi tradeStartDeadline, AVNU expiry (when present), Portals validated-order expiry (when present)
  • Relay / deBridge / ButterSwap / ArbitrumBridge / BOB Gateway / 0x / Cetus / Sun.io — no provider expiry exists; conservative 60s fallback (an early deadline is always safe — it just re-quotes)

Timestamps of undocumented resolution go through normalizeEpochToMs (unix-seconds vs ms is unambiguous at the 1e12 boundary), so a provider switching units can't produce a far-future deadline.

public-api propagation: QuoteResponse.expiresAt is now the swapper deadline instead of a fixed now+60s, with schema/guide docs warning that broadcasting after it risks failed swaps or, for deposit-style swappers, lost funds.

Quote store honors it: unsubmitted quotes live until deadline + 60min binding grace (a tx broadcast just before the deadline must still be able to bind its hash for status tracking, including clients that only bind after first confirmation on slow chains like Bitcoin) instead of a flat 15-minute TTL. Bound quotes keep the 60-minute execution-tracking window. The field being required on TradeQuote makes the compiler enforce that any future swapper supplies one.

Issue (if applicable)

closes #

Risk

Low-medium. No transaction construction or pricing changes — the field is additive metadata plus expiry bookkeeping. Client-visible change: API expiresAt values now vary by swapper (60s–6h instead of always 60s); clients honoring expiresAt as documented are unaffected. Quote-store retention now tracks the deadline, so THORChain-style quotes get ~75 min of binding window instead of 15, deadline-less swappers ~61 min instead of 15 — retention stays bounded by the store's max-size cap either way.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Quote responses (swapper package + public API) only.

Testing

Engineering

  • Live thornode/mayanode probes confirmed both return expiry (unix s, ~15 min out) alongside inbound_address — the protocol itself scopes inbound-address validity to that field.
  • Full provider-expiry survey across all 18 swapper integrations (field, units, file) drove the per-swapper policy; heterogeneous units (unix s, ISO strings, source-chain block heights) normalized to epoch ms.
  • Compile-driven completeness: deadline is required, so tsc proved every quote construction site populates it.
  • Swapper suite 17 files / 176 tests passing (THORChain fixtures now assert deadline = expiry × 1000); quote-store unit tests passing.
  • E2E through the local API, live providers: THORChain expiresAt = 14.8 min (thornode expiry), CoW = 29.9 min (validTo), Chainflip = 6.0h, NEAR Intents = 30.0 min (requested refund deadline, not the 72h address-inactive bound), 0x = exactly 60s (fallback).

Operations

  • Request quotes across swappers and confirm expiresAt varies as above rather than always ≈60s.
  • Status binding still works for a tx broadcast near the deadline (60 min grace).

Screenshots (if applicable)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Swap quotes now include provider-specific expiry deadlines, with safe fallback handling where needed.
    • Quote responses expose the actual deadline and indicate when a fresh quote is required.
    • Added validation for invalid, expired, or excessively distant quote deadlines.
  • Bug Fixes

    • Unsubmitted quotes remain available through a defined grace period after expiry.
    • Safeguards help prevent signing or broadcasting expired quotes and potential lost deposits.
  • Documentation

    • Updated REST API guidance with expiry examples and execution restrictions.

@kaladinlight
kaladinlight requested a review from a team as a code owner August 3, 2026 22:56
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 00db2c99-5c56-46c5-96b8-78eccc60f790

📥 Commits

Reviewing files that changed from the base of the PR and between e07760e and c81631e.

📒 Files selected for processing (1)
  • packages/swapper/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/swapper/package.json

📝 Walkthrough

Walkthrough

Trade quotes now require epoch-millisecond deadlines. Swapper integrations provide provider or fallback deadlines. The public API validates, stores, and returns these deadlines.

Changes

Quote deadline propagation

Layer / File(s) Summary
Deadline contract and normalization
packages/swapper/package.json, packages/swapper/src/types.ts, packages/swapper/src/utils/helpers.ts, packages/swapper/src/utils/helpers.test.ts, packages/swapper/src/utils/test-data/setupSwapQuote.ts
TradeQuote now requires a deadline. Shared helpers normalize timestamp units and provide a 60-second fallback.
Provider deadline propagation
packages/swapper/src/swappers/AcrossSwapper/..., packages/swapper/src/swappers/AvnuSwapper/..., packages/swapper/src/swappers/BebopSwapper/..., packages/swapper/src/swappers/CowSwapper/..., packages/swapper/src/swappers/NearIntentsSwapper/..., packages/swapper/src/swappers/PortalsSwapper/..., packages/swapper/src/swappers/StonfiSwapper/..., packages/swapper/src/swappers/ThorchainSwapper/..., src/state/apis/swapper/helpers/testData.ts
Provider expiry values now populate trade quote deadlines. Near Intents and Thorchain select bounded deadlines.
Fallback deadline assignment
packages/swapper/src/swappers/ArbitrumBridgeSwapper/..., packages/swapper/src/swappers/BobGatewaySwapper/..., packages/swapper/src/swappers/ButterSwap/..., packages/swapper/src/swappers/CetusSwapper/..., packages/swapper/src/swappers/ChainflipSwapper/..., packages/swapper/src/swappers/DebridgeSwapper/..., packages/swapper/src/swappers/RelaySwapper/..., packages/swapper/src/swappers/SunioSwapper/..., packages/swapper/src/swappers/ZrxSwapper/...
Integrations without provider expiry data use the shared fallback. Chainflip uses a six-hour channel deadline.
Public API expiry handling
packages/public-api/src/constants.ts, packages/public-api/src/lib/quoteStore.ts, packages/public-api/src/lib/quoteStore.test.ts, packages/public-api/src/routes/quote/getQuote.ts, packages/public-api/src/routes/quote/types.ts, packages/public-api/docs/rest-api-guide.md
The API rejects invalid or excessive deadlines, stores quotes through the deadline plus 60-minute binding grace, and returns the deadline as expiresAt.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Swapper
  participant PublicAPI
  participant QuoteStore
  Swapper->>PublicAPI: Return TradeQuote with deadline
  PublicAPI->>PublicAPI: Validate deadline bounds
  PublicAPI->>QuoteStore: Store quote through deadline plus binding grace
  PublicAPI-->>Swapper: Return expiresAt as deadline
Loading

Possibly related PRs

Poem

A rabbit checks each deadline,
In milliseconds, clear and fine.
Provider clocks and fallbacks meet,
The API keeps expiry neat.
Grace holds quotes through binding time,
Then hops away when clocks align.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: per-swapper quote deadlines and API quote-store enforcement.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/swapper-quote-deadlines

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts (1)

124-124: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use the shared epoch normalizer for both provider expiries.

Both sites multiply timestamps unconditionally. If either provider changes from seconds to milliseconds, the emitted TradeQuote.deadline becomes incorrect and can extend quote retention.

  • packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts#L124-L124: import normalizeEpochToMs and replace quote.expiry * 1000 with normalizeEpochToMs(quote.expiry).
  • packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts#L89-L91: import normalizeEpochToMs and replace tradeCommon.expiry * 1000 with normalizeEpochToMs(tradeCommon.expiry).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts` at
line 124, Use the shared normalizeEpochToMs helper for both provider expiry
conversions: in
packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts lines
124-124, import it and replace the quote.expiry multiplication; in
packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts lines 89-91, import
it and replace the tradeCommon.expiry multiplication. Preserve the resulting
TradeQuote.deadline values in milliseconds.
packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts (1)

166-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Declare the expiry parser return type.

The new inline function returns number | undefined but does not declare its return type. Add the annotation or extract a named helper.

Suggested annotation
-  const expiryMs = (() => {
+  const expiryMs = ((): number | undefined => {

As per coding guidelines, TypeScript functions must always declare explicit return types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts`
around lines 166 - 172, Add an explicit return type of number | undefined to the
inline expiry parser function assigned to expiryMs. Keep the existing parsing
and normalization behavior unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/public-api/src/routes/quote/getQuote.ts`:
- Line 196: Update getQuote to validate quote.deadline against the current time
immediately before quoteStore.set, rejecting the quote when it is already
expired; reuse the existing shared validation mechanism if available, and
preserve the existing expiry storage behavior for valid quotes.

In
`@packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts`:
- Around line 74-79: Update the deadline calculation in getTradeQuote so an
unparsable non-empty quote.deadline is treated as absent. Validate the parsed
provider deadline before passing it to Math.min, falling back to
Date.parse(quoteRequest.deadline) whenever it is non-finite, while preserving
the later-bound behavior for valid deadlines.

In `@src/state/apis/swapper/helpers/testData.ts`:
- Line 7: Replace the hard-coded deadline values in the test fixtures used by
thorQuote, cowQuote, and zrxQuote with a value derived from the test clock,
while keeping dedicated explicit deadlines only in tests that intentionally
verify expiration behavior.

---

Nitpick comments:
In `@packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts`:
- Line 124: Use the shared normalizeEpochToMs helper for both provider expiry
conversions: in
packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts lines
124-124, import it and replace the quote.expiry multiplication; in
packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts lines 89-91, import
it and replace the tradeCommon.expiry multiplication. Preserve the resulting
TradeQuote.deadline values in milliseconds.

In
`@packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts`:
- Around line 166-172: Add an explicit return type of number | undefined to the
inline expiry parser function assigned to expiryMs. Keep the existing parsing
and normalization behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7eb620b-b7a3-401a-a8f5-c0d6b02b1a8f

📥 Commits

Reviewing files that changed from the base of the PR and between 4a7be21 and a41c58c.

📒 Files selected for processing (34)
  • packages/public-api/docs/rest-api-guide.md
  • packages/public-api/src/lib/quoteStore.test.ts
  • packages/public-api/src/lib/quoteStore.ts
  • packages/public-api/src/routes/quote/getQuote.ts
  • packages/public-api/src/routes/quote/types.ts
  • packages/swapper/package.json
  • packages/swapper/src/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/AcrossSwapper/utils/getAcrossTradeContext.ts
  • packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/utils/getAvnuTradeContext.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts
  • packages/swapper/src/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/ChainflipSwapper/constants.ts
  • packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts
  • packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts
  • packages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/SunioSwapper/getSunioTradeQuote/getSunioTradeQuote.ts
  • packages/swapper/src/swappers/ThorchainSwapper/getTradeQuote/getTradeQuote.test.ts
  • packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts
  • packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts
  • packages/swapper/src/types.ts
  • packages/swapper/src/utils/helpers.ts
  • packages/swapper/src/utils/test-data/setupSwapQuote.ts
  • packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts
  • src/state/apis/swapper/helpers/testData.ts

Comment thread packages/public-api/src/routes/quote/getQuote.ts
Comment thread packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts Outdated
Comment thread src/state/apis/swapper/helpers/testData.ts Outdated
kaladinlight and others added 2 commits August 5, 2026 12:24
…store

Add a required deadline (epoch ms) to TradeQuote - the provider's own
expiry where supplied, a conservative 60s default otherwise:

- THORChain/MAYAChain: daemon quote expiry scoping the inbound address
  (longtail additionally bounded by the aggregator swapIn deadline)
- NearIntents: requested refund deadline (deposits must be mined before
  it; the response deadline is the later address-inactive bound)
- Chainflip: 6h against the ~24h deposit channel life
- CoW validTo, Bebop expiry, Across quoteExpiryTimestamp, Stonfi
  tradeStartDeadline, Avnu expiry, Portals validated-order expiry
- Relay/Debridge/ButterSwap/ArbitrumBridge/BobGateway/0x/Cetus/Sunio:
  60s fallback

public-api propagates it as the quote expiresAt (previously a fixed
now+60s) and the quote store keeps unsubmitted quotes until deadline
plus a 5min binding grace instead of a flat 15min TTL, so status
binding tracks real quote validity.

Swapper bumped to 19.0.0 (required field on TradeQuote).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 502 instead of storing/returning a quote whose deadline already passed
- NearIntents: treat an unparsable response deadline as absent instead of
  poisoning Math.min with NaN
- normalize provider-sourced expiries (thor daemon, bebop evm/solana,
  stonfi) through normalizeEpochToMs so a provider switching seconds to
  ms can never inflate a deadline; self-constructed and protocol-typed
  u32 values keep the explicit *1000
- derive test fixture deadlines from the clock instead of a fixed epoch
  that expires Jan 2027

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kaladinlight
kaladinlight force-pushed the feat/swapper-quote-deadlines branch from a41c58c to 6aea1b5 Compare August 5, 2026 18:29
@kaladinlight

Copy link
Copy Markdown
Member Author

@coderabbitai On the two nitpicks: took the epoch-normalizer suggestion and went further — all provider-sourced expiries (thor daemon, bebop EVM + Solana, stonfi) now go through normalizeEpochToMs, since the failure direction of a raw *1000 on a provider that switches to ms is a deadline in year ~56000, i.e. blessing a rotated THORChain inbound address. Self-constructed values (longtail aggregator deadline) and protocol-typed u32s (CoW validTo) keep the explicit *1000 — those can't be milliseconds by construction. Skipping the Portals return-type annotation: this codebase relies on inference for local helpers and there is no such lint rule configured.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@kaladinlight Confirmed. normalizeEpochToMs is the correct protection for provider-sourced expiry values. Keeping explicit * 1000 for values that are seconds by construction is appropriate.

I also accept the Portals return-type decision. The local helper follows the repository convention for inferred return types.

🐇


✏️ Learnings added
Learnt from: kaladinlight
URL: https://github.com/shapeshift/web/pull/12520

Timestamp: 2026-08-05T18:30:02.985Z
Learning: In the TypeScript codebase, local helper functions use inferred return types unless a specific requirement needs an explicit annotation. No lint rule requires return-type annotations for local helpers.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

kaladinlight and others added 3 commits August 5, 2026 13:48
Clients that only bind a txHash after first confirmation can exceed a
5min window on chains with 10min mean block times (P(block > 30min) is
~5% on bitcoin). Retention stays bounded by MAX_QUOTES either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep only the non-obvious ones: inbound address rotation, channel
margins, NearIntents request-vs-response semantics, the Portals
validate:false gap, and the longtail min.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

kaladinlight and others added 5 commits August 5, 2026 14:33
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live validated orders return expiry: null, so the parse currently
always falls through to the 60s fallback; keep the defensive parse for
when the typed optional field is populated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conditional min instead of the Infinity identity trick, with one
comment carrying both deadline semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
normalizeEpochToMs disambiguates seconds vs ms airtightly for future
timestamps, but a provider emitting micro/nanosecond epochs would pass
through as far-future - the one silent inflation vector. A 7d ceiling
at the enforcement point catches any unit bug regardless of cause; max
legitimate deadline today is chainflip's 6h.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

kaladinlight and others added 3 commits August 5, 2026 14:49
Same log-1000 banding - each unit occupies its own unambiguous window
for any realistic date, so the µs and ns folds carry the same certainty
as the original s/ms boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A future swapper legitimately quoting past 7d should hit a clear
checkpoint naming the constant to raise, not an opaque 502.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kaladinlight

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/state/apis/swapper/helpers/testData.ts (1)

4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use UPPER_SNAKE_CASE for the shared test constant.

Rename testQuoteDeadline to TEST_QUOTE_DEADLINE and update all three fixture assignments.

As per coding guidelines, constants and configuration values must use UPPER_SNAKE_CASE.

Also applies to: 95-95, 165-165

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/state/apis/swapper/helpers/testData.ts` around lines 4 - 9, Rename the
shared test constant testQuoteDeadline to TEST_QUOTE_DEADLINE in testData.ts,
and update all three fixture assignments to reference the new UPPER_SNAKE_CASE
identifier.

Source: Coding guidelines

packages/public-api/src/routes/quote/getQuote.ts (1)

195-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use structured metadata for the deadline sanity log.

The new console.error interpolates context into one string. Pass swapperName, deadline, and maxDeadline as structured fields.

As per coding guidelines, errors must use structured logging with relevant context and metadata.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/public-api/src/routes/quote/getQuote.ts` around lines 195 - 198,
Update the deadline sanity logging in getQuote to use structured metadata
instead of interpolating context into the message string. In the console.error
call within the quote.deadline check, provide swapperName, deadline, and
maxDeadline as distinct metadata fields while preserving the existing error
context.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/public-api/src/lib/quoteStore.ts`:
- Line 39: Update the QuoteStore constant BIND_GRACE_MS from one hour to the
contract’s five-minute value, 5 * 60 * 1000, preserving getQuote’s existing
retention logic.

In `@packages/public-api/src/routes/quote/getQuote.ts`:
- Around line 188-204: The quote deadline is not revalidated after the
asynchronous buildApprovalInfo step, allowing slow approval checks to store and
return an expired quote. In the getQuote flow, move or repeat the quote.deadline
validity check after buildApprovalInfo(step, sendAddress) completes and before
quoteStore.set and response validation, using the current time while preserving
the existing expired-quote error response.

---

Nitpick comments:
In `@packages/public-api/src/routes/quote/getQuote.ts`:
- Around line 195-198: Update the deadline sanity logging in getQuote to use
structured metadata instead of interpolating context into the message string. In
the console.error call within the quote.deadline check, provide swapperName,
deadline, and maxDeadline as distinct metadata fields while preserving the
existing error context.

In `@src/state/apis/swapper/helpers/testData.ts`:
- Around line 4-9: Rename the shared test constant testQuoteDeadline to
TEST_QUOTE_DEADLINE in testData.ts, and update all three fixture assignments to
reference the new UPPER_SNAKE_CASE identifier.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 086ec417-0ece-4f86-8ca7-d8b161e16bc1

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6456a and e5533d6.

📒 Files selected for processing (36)
  • packages/public-api/docs/rest-api-guide.md
  • packages/public-api/src/constants.ts
  • packages/public-api/src/lib/quoteStore.test.ts
  • packages/public-api/src/lib/quoteStore.ts
  • packages/public-api/src/routes/quote/getQuote.ts
  • packages/public-api/src/routes/quote/types.ts
  • packages/swapper/package.json
  • packages/swapper/src/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/AcrossSwapper/utils/getAcrossTradeContext.ts
  • packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/utils/getAvnuTradeContext.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts
  • packages/swapper/src/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/ChainflipSwapper/constants.ts
  • packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts
  • packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts
  • packages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/SunioSwapper/getSunioTradeQuote/getSunioTradeQuote.ts
  • packages/swapper/src/swappers/ThorchainSwapper/getTradeQuote/getTradeQuote.test.ts
  • packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts
  • packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts
  • packages/swapper/src/types.ts
  • packages/swapper/src/utils/helpers.test.ts
  • packages/swapper/src/utils/helpers.ts
  • packages/swapper/src/utils/test-data/setupSwapQuote.ts
  • packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts
  • src/state/apis/swapper/helpers/testData.ts
🚧 Files skipped from review as they are similar to previous changes (30)
  • packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/ChainflipSwapper/constants.ts
  • packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts
  • packages/public-api/docs/rest-api-guide.md
  • packages/swapper/src/types.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts
  • packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts
  • packages/swapper/src/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts
  • packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.ts
  • packages/public-api/src/routes/quote/types.ts
  • packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/utils/test-data/setupSwapQuote.ts
  • packages/swapper/package.json
  • packages/swapper/src/swappers/SunioSwapper/getSunioTradeQuote/getSunioTradeQuote.ts
  • packages/public-api/src/lib/quoteStore.test.ts
  • packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts
  • packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts
  • packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts
  • packages/swapper/src/swappers/AcrossSwapper/utils/getAcrossTradeContext.ts
  • packages/swapper/src/utils/helpers.ts
  • packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts
  • packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts
  • packages/swapper/src/swappers/AvnuSwapper/utils/getAvnuTradeContext.ts
  • packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts
  • packages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.ts

Comment thread packages/public-api/src/lib/quoteStore.ts
Comment thread packages/public-api/src/routes/quote/getQuote.ts
A slow buildApprovalInfo allowance check could otherwise store and
return a quote whose deadline had already passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kaladinlight
kaladinlight enabled auto-merge (squash) August 6, 2026 16:28
@kaladinlight
kaladinlight disabled auto-merge August 7, 2026 14:00
@kaladinlight
kaladinlight merged commit 0bdb61b into develop Aug 7, 2026
1 check passed
@kaladinlight
kaladinlight deleted the feat/swapper-quote-deadlines branch August 7, 2026 14:00
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