Skip to content

improve(gasless): enforce deadline buffer before deposit submission and immediate fill#3463

Open
droplet-rl wants to merge 1 commit into
masterfrom
droplet/T90K0AL22-C0AB30NK4LX-1780657425-757939
Open

improve(gasless): enforce deadline buffer before deposit submission and immediate fill#3463
droplet-rl wants to merge 1 commit into
masterfrom
droplet/T90K0AL22-C0AB30NK4LX-1780657425-757939

Conversation

@droplet-rl

Copy link
Copy Markdown
Contributor

Summary

Hardens the gasless relayer against two failure modes where a partial action leaves the relayer holding an unrecoverable position:

  • Standard path (non-immediate, non-CCTP) — before submitting an origin deposit, require the destination fillDeadline to be more than 2 minutes out. Otherwise the deposit may land but the subsequent fill fails for lack of time, leaving an unfillable origin deposit.
  • Immediate-fill path — before firing the destination fill ahead of deposit confirmation, require the origin authorization deadline (EIP-3009 validBefore or Permit2 deadline) to be more than 2 minutes out, unless the deposit has already been observed on chain. Otherwise the fill may land while the authorization expires, leaving an unreimbursable fill.

When the immediate path is blocked by the authorization deadline, the message falls back to the standard path (where the fillDeadline check then applies). A single shared constant (GASLESS_DEADLINE_BUFFER_SECONDS = 120) controls both checks.

The CCTP path is exempted from the fillDeadline check since there is no relayer-submitted destination fill.

Test plan

  • yarn typecheck
  • yarn lint
  • yarn test --grep "Gasless" — 52 pass, including 5 new cases under Deadline buffer checks
  • Observe in pre-prod that messages near fillDeadline log the new warn and skip deposit submission rather than landing unfillable deposits

🤖 Generated with Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9535d33b56

ℹ️ 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".

Comment on lines +712 to +716
if (
!fillImmediate &&
!isCctpDeposit &&
fillDeadline - getCurrentTime() <= GASLESS_DEADLINE_BUFFER_SECONDS
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow observed deposits to continue to fill

When a standard bridge deposit is already observed on origin but its destination fill is not, _markFilledFromInitialObservation leaves it eligible for this state machine so the bot can finish the fill. With this new guard, any such deposit whose fillDeadline has less than the buffer remaining is moved to ERROR before DEPOSIT_CONFIRM can resolve the already-landed deposit, so restarts or delayed polling can strand a valid on-chain deposit even though there is still time to submit the fill. The buffer should only prevent submitting a new origin deposit, or should exempt observedDeposits[originChainId].has(depositKey).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — addressed in 3965c03. Exempted observedDeposits[originChainId].has(depositKey) from the buffer check so deposits already landed on origin (e.g. seen via _markFilledFromInitialObservation after a restart) still proceed to a fill attempt even when the remaining fillDeadline window is short. New test Standard path: still proceeds when fillDeadline within buffer but deposit already on chain covers it.

Comment on lines +712 to +716
if (
!fillImmediate &&
!isCctpDeposit &&
fillDeadline - getCurrentTime() <= GASLESS_DEADLINE_BUFFER_SECONDS
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply the fill deadline buffer to immediate fills

For instantFill deposits where fillImmediate remains true, this condition skips the new fillDeadline buffer entirely, so a message with only a few seconds left before fillDeadline still submits the origin deposit and then attempts the destination fill. If that fill misses the on-chain deadline, the bot has already landed a deposit it can no longer fill, which is the same scenario the standard-path buffer is trying to avoid.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed — also addressed in 3965c03. Dropped the !fillImmediate clause so the fillDeadline buffer applies to the immediate-fill path too. If fillDeadline is within the buffer and the deposit is not already on chain, the message now ERRORs before any origin submission, so we never land a deposit we cannot subsequently fill. New test Immediate fill: ERROR when fillDeadline within buffer and deposit not observed covers it.

@droplet-rl

Copy link
Copy Markdown
Contributor Author

@codex addressed both review items in 3965c03:

  • P1 (observed deposits) — added !depositObserved to the fillDeadline buffer check so deposits already on origin (e.g. picked up by _markFilledFromInitialObservation after a restart) can still proceed to a fill attempt.
  • P2 (immediate fills) — dropped the !fillImmediate clause so the fillDeadline buffer now also gates the immediate-fill path; if the destination window is too short and the deposit is not yet on chain, the message ERRORs before any origin submission.

Two new tests cover the added behavior (Standard path: still proceeds when fillDeadline within buffer but deposit already on chain, Immediate fill: ERROR when fillDeadline within buffer and deposit not observed). Full GaslessRelayer suite is green (40 passing); typecheck and lint also clean. Please re-review.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ 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".

@droplet-rl

Copy link
Copy Markdown
Contributor Author

Thanks — re-review confirms the buffer fixes are clean. No further changes.

… immediate fill

Two guards to prevent the relayer from acting on a deposit whose deadlines
won't outlive the action:

- Standard (non-immediate, non-CCTP) path: refuse to submit a deposit when
  fewer than GASLESS_DEADLINE_BUFFER_SECONDS remain on the destination
  fillDeadline. Otherwise the deposit may land but the subsequent fill misses
  the destination window.
- Immediate-fill path: refuse to commit a destination fill when fewer than
  GASLESS_DEADLINE_BUFFER_SECONDS remain on the origin authorization
  (EIP-3009 validBefore or Permit2 deadline), unless the deposit is already
  observed on chain. The check lives inside fillImmediate() as one of the
  existing eligibility predicates rather than a downstream override.
@droplet-rl droplet-rl force-pushed the droplet/T90K0AL22-C0AB30NK4LX-1780657425-757939 branch from 784a8c3 to 6dbbeed Compare June 8, 2026 15:28
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