Skip to content

fix(intents): close funds-handling gaps in the escrow order flow - #28

Merged
Timidan merged 1 commit into
feat/lifi-intentsfrom
fix/intents-hardening
Aug 23, 2026
Merged

fix(intents): close funds-handling gaps in the escrow order flow#28
Timidan merged 1 commit into
feat/lifi-intentsfrom
fix/intents-hardening

Conversation

@Timidan

Copy link
Copy Markdown
Owner

Six defects found while reviewing the intents integration before merge. The first three can cost users funds; the rest remove footguns.

Six defects found while reviewing the intents integration before merge.
The first three can cost users funds; the rest remove footguns.
- Reject non-positive quote outputs. `amount` is a decimal string, so the
`!previewAmount` guard let "0" through and built an order offering the
whole input for nothing, which a solver can fill by transferring zero.
Adds readQuoteOutputAmount() and uses it in all three quote paths.
- Bind the order to the signing account. open() collects from msg.sender
but delivers and refunds to order.user; the reset effects keyed on the
`recipient` prop and not the connected address, so switching accounts
after quoting let one account fund an order that pays another. Adds
`address` to the reset deps and asserts the two match before signing.
- Parse validUntil by shape. It arrives as a numeric Unix timestamp but
was typed as a string and fed to Date.parse, which returns NaN for
numeric input in either unit — the real expiry was discarded and
replaced by an arbitrary now+15m.
- Revalidate the fill window immediately before open(). Deadlines were
fixed at quote time and never rechecked, and legs open sequentially, so
a later leg could escrow into an order no solver would fill.
- Record the open() hash before awaiting its receipt, on both the step
components and the concierge pipeline. A receipt timeout on a tx that
later mined left the escrow invisible, and retry minted a fresh nonce —
two live orders, both fillable. Retry now refuses while a hash is held.
- Approve the requested amount instead of MaxUint256 in the composer
helper, and stop treating an unreadable allowance as zero, which
skipped the USDT-style reset and sent a reverting approve.
CopilotAI lite review requested due to automatic review settings August 23, 2026 04:52
@vercel

vercelBot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
web3-toolkitReadyReadyPreviewAug 23, 2026 4:52am

CopilotAI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connectorchatgpt-codex-connectorBot 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:a7d2827cc1

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

);
}

assertFillWindowOpen(order.fillDeadline);

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 Recheck the fill deadline after approvals

When the ERC-20 allowance is insufficient—especially for a token requiring a reset transaction—this check runs before the destination balance read and one or two approval confirmations. A quote with slightly more than 30 seconds remaining can therefore expire before open() is submitted, leaving funds in an order that cannot be filled until refund expiry. Move or repeat the assertion immediately before sendTransaction; the same premature ordering also occurs in WithdrawIntentRouteStep.handleOpen and useIntentLegPipeline.openOne.

Useful? React with 👍 / 👎.

Comment on lines +714 to +716
throw new Error(
"Couldn't read the current token allowance — refusing to approve blindly. Try again in a moment.",
);

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 Report allowance-read failures through the recovery state

If this RPC read fails after the bridge has settled, throwing directly bypasses fail(), which is responsible for emitting phase: "failed", failedAfterBridge, and the bridge metadata. DepositFlow consequently retains phase: "quoting-deposit", treats the operation as perpetually in flight, disables the main CTA, and never exposes the retry-deposit action even though the user's funds have already reached the destination chain. Route this error through fail() as the surrounding failure paths do.

Useful? React with 👍 / 👎.

Comment on lines +359 to +361
// Record before waiting: a receipt timeout on a tx that later mines must
// not leave the escrow invisible, or retry would open a second order.
setOpenTxHash(hash);

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 Clear the broadcast lock after a confirmed revert

When the receipt resolves with status === "reverted", this hash has already been persisted; the catch then enters failed, while the retry button is disabled whenever openTxHash is set. A confirmed revert cannot later create an escrow, so retaining the timeout-oriented lock unnecessarily prevents obtaining another intent quote until the component is reset or reloaded. Clear the hash on the explicit reverted-receipt path, with the analogous path in WithdrawIntentRouteStep needing the same treatment.

Useful? React with 👍 / 👎.

@Timidan
Timidan merged commit 094e5f8 into feat/lifi-intentsAug 23, 2026
2 checks passed
Sign up for freeto 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

@Timidan