feat(swapper): per-swapper quote deadlines, honored by the api quote store - #12520
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughTrade quotes now require epoch-millisecond deadlines. Swapper integrations provide provider or fallback deadlines. The public API validates, stores, and returns these deadlines. ChangesQuote deadline propagation
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
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts (1)
124-124: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winUse the shared epoch normalizer for both provider expiries.
Both sites multiply timestamps unconditionally. If either provider changes from seconds to milliseconds, the emitted
TradeQuote.deadlinebecomes incorrect and can extend quote retention.
packages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.ts#L124-L124: importnormalizeEpochToMsand replacequote.expiry * 1000withnormalizeEpochToMs(quote.expiry).packages/swapper/src/utils/thorchain/getThorL1TradeQuote.ts#L89-L91: importnormalizeEpochToMsand replacetradeCommon.expiry * 1000withnormalizeEpochToMs(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 winDeclare the expiry parser return type.
The new inline function returns
number | undefinedbut 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
📒 Files selected for processing (34)
packages/public-api/docs/rest-api-guide.mdpackages/public-api/src/lib/quoteStore.test.tspackages/public-api/src/lib/quoteStore.tspackages/public-api/src/routes/quote/getQuote.tspackages/public-api/src/routes/quote/types.tspackages/swapper/package.jsonpackages/swapper/src/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/AcrossSwapper/utils/getAcrossTradeContext.tspackages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/AvnuSwapper/utils/getAvnuTradeContext.tspackages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.tspackages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.tspackages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.tspackages/swapper/src/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/ChainflipSwapper/constants.tspackages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.tspackages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.tspackages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/SunioSwapper/getSunioTradeQuote/getSunioTradeQuote.tspackages/swapper/src/swappers/ThorchainSwapper/getTradeQuote/getTradeQuote.test.tspackages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.tspackages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.tspackages/swapper/src/types.tspackages/swapper/src/utils/helpers.tspackages/swapper/src/utils/test-data/setupSwapQuote.tspackages/swapper/src/utils/thorchain/getThorL1TradeQuote.tssrc/state/apis/swapper/helpers/testData.ts
…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>
a41c58c to
6aea1b5
Compare
|
@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 |
|
Tip For best results, initiate chat on the files or code changes.
I also accept the Portals return-type decision. The local helper follows the repository convention for inferred return types. 🐇 ✏️ Learnings added
You are interacting with an AI system. |
|
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. |
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>
|
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. |
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>
|
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. |
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>
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
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. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/state/apis/swapper/helpers/testData.ts (1)
4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse UPPER_SNAKE_CASE for the shared test constant.
Rename
testQuoteDeadlinetoTEST_QUOTE_DEADLINEand 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 winUse structured metadata for the deadline sanity log.
The new
console.errorinterpolates context into one string. PassswapperName,deadline, andmaxDeadlineas 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
📒 Files selected for processing (36)
packages/public-api/docs/rest-api-guide.mdpackages/public-api/src/constants.tspackages/public-api/src/lib/quoteStore.test.tspackages/public-api/src/lib/quoteStore.tspackages/public-api/src/routes/quote/getQuote.tspackages/public-api/src/routes/quote/types.tspackages/swapper/package.jsonpackages/swapper/src/swappers/AcrossSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/AcrossSwapper/utils/getAcrossTradeContext.tspackages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/AvnuSwapper/utils/getAvnuTradeContext.tspackages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.tspackages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.tspackages/swapper/src/swappers/BebopSwapper/utils/getBebopTradeContext.tspackages/swapper/src/swappers/BobGatewaySwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/ChainflipSwapper/constants.tspackages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.tspackages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.tspackages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/SunioSwapper/getSunioTradeQuote/getSunioTradeQuote.tspackages/swapper/src/swappers/ThorchainSwapper/getTradeQuote/getTradeQuote.test.tspackages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.tspackages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.tspackages/swapper/src/types.tspackages/swapper/src/utils/helpers.test.tspackages/swapper/src/utils/helpers.tspackages/swapper/src/utils/test-data/setupSwapQuote.tspackages/swapper/src/utils/thorchain/getThorL1TradeQuote.tssrc/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
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>
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
expiresAtof 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: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)deadline(~72h, when the deposit address goes fully inactive) is only used if it is somehow tightersourceExpiryBlockis a source-chain height we can't cheaply convert to time, and channels live ~24h, so 6h keeps a 4× marginvalidTo, Bebopexpiry(EVM + Solana sealed tx), AcrossquoteExpiryTimestamp, Ston.fitradeStartDeadline, AVNUexpiry(when present), Portals validated-orderexpiry(when present)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.expiresAtis 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 + 60minbinding 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 onTradeQuotemakes 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
expiresAtvalues now vary by swapper (60s–6h instead of always 60s); clients honoringexpiresAtas 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.Quote responses (swapper package + public API) only.
Testing
Engineering
expiry(unix s, ~15 min out) alongsideinbound_address— the protocol itself scopes inbound-address validity to that field.deadlineis required, sotscproved every quote construction site populates it.deadline = expiry × 1000); quote-store unit tests passing.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
expiresAtvaries as above rather than always ≈60s.Screenshots (if applicable)
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation