Skip to content

fix(swap-service): drop the block time tolerance and gate payouts on attribution - #65

Merged
kaladinlight merged 3 commits into
developfrom
fix/drop-block-time-tolerance
Sep 9, 2026
Merged

kaladinlight merged 3 commits into
developfrom
fix/drop-block-time-tolerance

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Description

Two related changes to how a swap's quote binding is decided, and to whether anything consumes it.

Drop BLOCK_TIME_TOLERANCE_MS. The tolerance extended the accept window past a transaction's block timestamp. It was sized on the reasoning that it cost nothing, but the boundary it moves is the only thing separating a quote that preceded its transaction from one that did not, so widening it is not free. It has also never been used: all 28 accepted swaps in production read quote-precedes-tx and none read quote-within-tolerance, because backfilled rows carry their own headroom and a real quote precedes its own broadcast. The block timestamp is now the whole boundary.

The case the tolerance was meant to cover — a miner-declared block timestamp lagging the broadcast it carries — remains observable without it. Every rejection records blockTime and quotedAt in attributionDetails, so the overshoot is queryable, and the payout run lists each unaccepted swap with its reason. If real rows ever cluster near zero, the constant can be reintroduced sized from that evidence rather than from assumption.

Gate payouts on attribution. scripts/affiliate-payouts consulted verificationStatus but never attributionStatus, so a swap whose binding had been rejected was still payable as long as its fee verified — the verdicts were written but nothing acted on them. Attribution now gates ahead of verification, since whether a claim is a partner's to be paid precedes whether its fee checked out. Unaccepted swaps join the existing review items with their status and reason, surfacing per-swap rather than as a bare count.

The gate requires ACCEPTED rather than excluding REJECTED, so it fails closed — PENDING and DISPUTED are withheld too. Withholding is reversible; paying is not. One consequence worth knowing: a newly created swap is unpayable until the attribution pass reaches it, so a payout run overlapping that window lists those rows as unattributed rather than paying them.

No query changes were needed; findMany already returned these columns unused.

Testing

  • apps/swap-service: 98 tests pass. Tolerance cases were replaced with the boundary itself — a quote one second past its block now rejects.
  • scripts/affiliate-payouts: 26 tests pass, including a new case asserting REJECTED, PENDING and DISPUTED are all withheld with their reasons carried into the review items.
  • Verified against production data: 28 accepted rows, none of which used the tolerance.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P36At9muenEn9Uqzjo1rDw

kaladinlight and others added 2 commits September 9, 2026 12:04
The window was sized on the reasoning that it cost nothing, which was
wrong: it is exactly how long a second quote has to bind cleanly to a
transaction someone already claimed. No row ever used it - all 28
accepted swaps in production read quote-precedes-tx - because backfilled
rows carry their own headroom and a real quote precedes its broadcast.

A rejection close enough to be miner clock skew is logged instead, so
the case that motivated the tolerance is visible if it ever happens.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P36At9muenEn9Uqzjo1rDw
The payout script consulted verification but never attribution, so a
rejected or contested claim was payable as long as its fee verified.
Attribution now gates ahead of verification and unaccepted swaps join the
review items, which is where an operator already looks before paying.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P36At9muenEn9Uqzjo1rDw
@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d1febb4e-425f-466c-ab78-09ceb4fe2c6a

📥 Commits

Reviewing files that changed from the base of the PR and between a386bc5 and 9daa0df.

📒 Files selected for processing (7)
  • apps/swap-service/src/swaps/__tests__/utils.test.ts
  • apps/swap-service/src/swaps/constants.ts
  • apps/swap-service/src/swaps/utils.ts
  • scripts/affiliate-payouts/affiliate-payouts.test.ts
  • scripts/affiliate-payouts/affiliate-payouts.ts
  • scripts/affiliate-payouts/types.ts
  • scripts/affiliate-payouts/utils.ts
💤 Files with no reviewable changes (1)
  • apps/swap-service/src/swaps/constants.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The swap service now rejects quotes that postdate transaction blocks. The affiliate payout pipeline separates non-accepted attribution into an unattributed category, warnings, totals, summaries, and tests.

Changes

Quote binding validation

Layer / File(s) Summary
Quote boundary validation
apps/swap-service/src/swaps/constants.ts, apps/swap-service/src/swaps/utils.ts, apps/swap-service/src/swaps/__tests__/utils.test.ts
The block-time tolerance constant and acceptance branch were removed. Quotes that postdate their block by one second are rejected with quote-postdates-tx. Tests cover the updated boundary.

Affiliate attribution handling

Layer / File(s) Summary
Unattributed payout contracts
scripts/affiliate-payouts/types.ts
The payout types now include unattributed swaps, warning entries, aggregate results, and totals.
Attribution aggregation and records
scripts/affiliate-payouts/utils.ts
Non-accepted attribution statuses are collected before verification and fee processing. Record construction emits unattributed warnings and counts.
Payout wiring and validation
scripts/affiliate-payouts/affiliate-payouts.ts, scripts/affiliate-payouts/affiliate-payouts.test.ts
The payout pipeline passes and prints unattributed swaps. Tests cover rejected, pending, and disputed attribution statuses.

Priority: ⬇️ Low

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

Merge Risk: ⚪ Minimal · up to 9daa0

Post-block quotes are rejected, and unsettled affiliate attributions are withheld from payouts while remaining visible for review. No concrete current-head merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant SwapRows
  participant aggregateByPartner
  participant buildRecord
  participant PayoutSummary
  SwapRows->>aggregateByPartner: rows with attribution status
  aggregateByPartner->>buildRecord: unattributed swaps
  buildRecord->>PayoutSummary: unattributedSwaps total
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes both primary changes: removing the swap-service block time tolerance and requiring accepted attribution for payouts.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/drop-block-time-tolerance

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

A rabbit checks each quoted time,
And marks late claims in a line.
Unsettled swaps leave payouts clear,
Their warnings and totals now appear.
The ledger hops in bounds just fine.

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

The payout run already lists every unaccepted swap with its status and
reason, and the overshoot is derivable from attributionDetails, so the
warning restated in logs what the review items say where it matters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P36At9muenEn9Uqzjo1rDw
@kaladinlight
kaladinlight merged commit 4f32c29 into develop Sep 9, 2026
2 checks passed
@kaladinlight
kaladinlight deleted the fix/drop-block-time-tolerance branch September 9, 2026 18:56
@kaladinlight kaladinlight changed the title fix(swap-service): drop the block time tolerance and gate payouts on attribution fix(swap-service): settle contested claims, drop the block time tolerance, and gate payouts on attribution Sep 9, 2026
@kaladinlight kaladinlight changed the title fix(swap-service): settle contested claims, drop the block time tolerance, and gate payouts on attribution fix(swap-service): drop the block time tolerance and gate payouts on attribution Sep 9, 2026
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