Skip to content

fix(trade): preserve URL recipient when chainId initializes - #7465

Merged
alfetopito merged 11 commits into
cowprotocol:developfrom
tenderdeve:fix/7323-prefill-recipient-from-url
Jun 8, 2026
Merged

alfetopito merged 11 commits into
cowprotocol:developfrom
tenderdeve:fix/7323-prefill-recipient-from-url

Conversation

@tenderdeve

@tenderdeve tenderdeve commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

When a URL contains a ?recipient= parameter (e.g. /#/1/swap/WETH/COW?recipient=vitalik.eth), the recipient field was cleared on page load before the user could interact with it.

The chain-change effect in useResetRecipient fires as the wallet's chainId initializes from undefined to its real value, unconditionally calling onChangeRecipient(null) and wiping the URL-provided recipient before it could stick.
Adding hasRecipientInUrl as a guard prevents the reset when recipient was provided via URL.

Fixes #7323

Summary

Fixes #7323

Added !hasRecipientInUrl condition to the chain-change reset effect in useResetRecipient so URL-provided recipients are preserved on page load.

To Test

  1. Open CoW Swap with a recipient in the URL: /#/1/swap/WETH/COW?recipient=vitalik.eth
  • Recipient field should be pre-filled with vitalik.eth on page load
  • Recipient field should not be cleared after wallet connects and chainId initializes
  1. Open CoW Swap without a recipient in the URL
  • Recipient field should remain empty on page load (existing behaviour unchanged)
  1. Switch chains while a recipient is set manually (not via URL)
  • Recipient field should still be cleared on chain switch (existing behaviour unchanged)

Background

useResetRecipient has a dedicated effect that resets the recipient whenever chainId changes. On initial page load, the wallet's chainId transitions from undefined to its real value, which triggers this effect. At that point hasRecipientInUrl is already true (parsed from the URL atom), but the effect had no guard for it — so onChangeRecipient(null) fired unconditionally, clearing the field.

The fix adds hasRecipientInUrl to both the condition and the dependency array of that specific effect. The initial-load effect already had this guard; only the chain-change effect was missing it

Summary by CodeRabbit

  • Bug Fixes
    • Preserve recipients provided in the URL so they are no longer cleared by resets, hook exits, or override removal.
    • Prevent unintended clearing when switching chains by validating recipients only after initial app hydration.
    • Stop post-hook or non-supported bridge logic from removing URL-specified recipients.

When a user navigates to a URL containing ?recipient=vitalik.eth,
the recipient was being cleared on page load because the chain
change effect in useResetRecipient fired as the wallet's chainId
went from undefined to its initial value.

Add hasRecipientInUrl as a guard so the chain change reset is skipped
when the recipient was explicitly provided in the URL.

Fixes cowprotocol#7323
@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

@tenderdeve is attempting to deploy a commit to the cow-dev Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 7, 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 014ed2b8-73ac-485a-a667-1502e2f1d058

📥 Commits

Reviewing files that changed from the base of the PR and between f958180 and 6431b36.

📒 Files selected for processing (1)
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts

Walkthrough

Recipient reset logic now detects URL-provided recipients synchronously from query params and preserves them across reset effects; chain-change and hook-exit effects are gated on that detection. The chain-change hook replaces mount-skip logic with an initialization ref that ignores the first undefined→defined hydration.

Changes

Recipient URL Prefill Preservation

Layer / File(s) Summary
URL recipient detection setup
apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts
Imports useLocation and useMemo and computes hasRecipientInUrl from location.search, checking recipient and recipientAddress query parameters.
Reset effects with URL preservation gating
apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts
Refactors recipient-reset effects: chainId-change effect clears recipient only when no URL recipient is present and when not blocked by postHooksRecipientOverride/non-EVM bridging; recipient-override-removal, hooks-store exit, and hooks trade-type/native-in effects also skip clearing when hasRecipientInUrl is true. Dependency arrays updated accordingly.
Initial transition gating refactor
apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipientOnChainChange.ts
Replaces mount-based skip with a hasInitialized ref that consumes the first undefined→defined targetChainId hydration and thereafter validates the recipient, calling onChangeRecipient(null) when invalid for subsequent chain changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I sniffed the query, a ribbon in the log,
A recipient nestled safe beneath the fog.
Chains may tumble, mounts may skip and prance,
But I keep your address steady through the dance.
Hoppity-hop—your link stays in the swap! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: preventing URL-provided recipients from being cleared when chainId initializes.
Description check ✅ Passed The description follows the template with Summary, To Test, and Background sections, providing clear context and comprehensive testing steps.
Linked Issues check ✅ Passed The PR successfully addresses issue #7323 by adding hasRecipientInUrl guards to preserve URL-provided recipients and prevent clearing on chainId initialization.
Out of Scope Changes check ✅ Passed Both modified files (useResetRecipient.ts and useResetRecipientOnChainChange.ts) are directly scoped to fixing the recipient preservation issue described in #7323.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@vercel

vercel Bot commented May 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
explorer-dev Ready Ready Preview Jun 5, 2026 10:59am
swap-dev Ready Ready Preview Jun 5, 2026 10:59am

Request Review

@vercel
vercel Bot temporarily deployed to Preview – swap-dev May 7, 2026 09:06 Inactive

@elena-zh elena-zh 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.

Hey @tenderdeve , thank you for the contribution!
Unfortunately, this does not fix the issue:

Image

@tenderdeve
tenderdeve force-pushed the fix/7323-prefill-recipient-from-url branch from 5c190c9 to 618c26c Compare May 14, 2026 05:02
The previous guard derived hasRecipientInUrl from the tradeStateFromUrl
atom, which is only populated one render after useResetRecipient first
runs. On the first commit the reset effects therefore saw an empty value
and cleared a recipient that was explicitly provided in the URL.

- Derive hasRecipientInUrl synchronously from location.search so it is
  correct on the first render, and apply the guard to every reset effect
  rather than only the chainId-change one.
- In useResetRecipientOnChainChange, skip the initial
  `undefined -> chainId` transition so app init is not treated as a
  user-driven chain change.

Fixes cowprotocol#7323
@tenderdeve

Copy link
Copy Markdown
Contributor Author

@elena-zh hey, Thanks for catching that — you were right, the first patch didn't fix it.

fixed and Tested locally with ?recipient=vitalik.eth, ?recipient=0x…, and no param — recipient is preserved in the first two, stays empty in the last, no console errors.

One known limitation worth flagging: targetChainId comes from the buy token, so a chainId → undefined → chainId sequence (buy token cleared then reselected) also gets skipped. Rare in practice, but happy to tighten the guard to track "first real chain seen".

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

🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts (1)

66-79: ⚡ Quick win

Remove unused dependency isNativeIn.

The isNativeIn variable is included in the dependency array (line 76) but is not referenced anywhere in the effect body. This causes the effect to re-run unnecessarily whenever isNativeIn changes, even though it doesn't affect the effect's logic.

♻️ Remove extraneous dependency
  }, [
    recipient,
    postHooksRecipientOverride,
    prevPostHooksRecipientOverride,
-   isNativeIn,
    hasRecipientInUrl,
    onChangeRecipient,
  ])
🤖 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 `@apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts` around
lines 66 - 79, The useEffect contains an unused dependency isNativeIn causing
unnecessary re-runs; update the effect declared with useEffect so its dependency
array only includes the values actually referenced in the body (recipient,
postHooksRecipientOverride, prevPostHooksRecipientOverride, hasRecipientInUrl,
onChangeRecipient) and remove isNativeIn, ensuring the effect logic around
recipientOverrideWasRemoved and onChangeRecipient(null) remains unchanged.
🤖 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.

Nitpick comments:
In `@apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts`:
- Around line 66-79: The useEffect contains an unused dependency isNativeIn
causing unnecessary re-runs; update the effect declared with useEffect so its
dependency array only includes the values actually referenced in the body
(recipient, postHooksRecipientOverride, prevPostHooksRecipientOverride,
hasRecipientInUrl, onChangeRecipient) and remove isNativeIn, ensuring the effect
logic around recipientOverrideWasRemoved and onChangeRecipient(null) remains
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 64d0a732-c986-4c4e-8bcf-8afc4123741a

📥 Commits

Reviewing files that changed from the base of the PR and between 88dd7c2 and f134230.

📒 Files selected for processing (2)
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipientOnChainChange.ts

@elena-zh
elena-zh requested review from a team and elena-zh May 14, 2026 09:57

@elena-zh elena-zh 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.

@tenderdeve

Copy link
Copy Markdown
Contributor Author

@elena-zh Hey I tried to test it on the localhost and it is working. Maybe i am not following the correct way to reproduce the bug, can you please help me out with the steps to reproduce the bugs that you are following.
Thanks.
I am attaching the video for reference below
https://canva.link/9gr3blwlv9oxvw0

@elena-zh

Copy link
Copy Markdown
Contributor

@elena-zh Hey I tried to test it on the localhost and it is working. Maybe i am not following the correct way to reproduce the bug, can you please help me out with the steps to reproduce the bugs that you are following. Thanks. I am attaching the video for reference below https://canva.link/9gr3blwlv9oxvw0

Hey @tenderdeve , I've provided you the test link in my comment above #7465 (review)

@tenderdeve

Copy link
Copy Markdown
Contributor Author

@elena-zh, Hey did we redeploy the code after my second fix. I think we are still testing either with old commit or the deployment is not reflecting the latest change. because i have tried to test it on localhost and it works correctly, as you can see in the video i have attached in the previous comment.

@vercel
vercel Bot temporarily deployed to Preview – swap-dev May 19, 2026 08:49 Inactive
@elena-zh

Copy link
Copy Markdown
Contributor

@tenderdeve is attempting to deploy a commit to the cow-dev Team on Vercel.

A member of the Team first needs to authorize it.

Hey @tenderdeve , could you please sigh the CLA?

@vercel
vercel Bot temporarily deployed to Preview – swap-dev May 19, 2026 10:21 Inactive
@elena-zh
elena-zh self-requested a review May 19, 2026 10:28

@elena-zh elena-zh 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.

@elena-zh, Hey did we redeploy the code after my second fix. I think we are still testing either with old commit or the deployment is not reflecting the latest change. because i have tried to test it on localhost and it works correctly, as you can see in the video i have attached in the previous comment.

I've redeployed the changes: now it LGTM.
Will approve the PR once you sign CLA.

@tenderdeve

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@tenderdeve

Copy link
Copy Markdown
Contributor Author

@elena-zh, Sure just signed CLA, btw i had signed the CLA for a PR before as well which was merged. ( #7464 )

@elena-zh elena-zh 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.

Tnx

@fairlighteth fairlighteth 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.

Thank you for this PR. Please review these AI findings:

  • P2 apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipientOnChainChange.ts:30
    The new if (prev === undefined) return skips every undefined -> chainId transition, not just app initialization. If the buy token is cleared or temporarily unset, then the user selects BTC/SOL, recipient validation is skipped and an incompatible existing recipient can remain. Track the one initial hydration skip separately, then validate later undefined -> chainId transitions.

  • Nit apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts:76
    CodeRabbit’s unused isNativeIn dependency comment is still valid. It is not a blocker, but removing it avoids unnecessary effect reruns.

Skip only the initial hydration (undefined -> chainId at app start) once,
then validate every subsequent target chain change. Previously the
undefined-guard skipped all undefined -> chainId transitions, so clearing
the buy token then selecting a non-EVM chain (BTC/SOL) left an incompatible
recipient in place.

Also drop the unused isNativeIn dependency from the hook-removal effect.
@tenderdeve

Copy link
Copy Markdown
Contributor Author

@fairlighteth hey, did those changes as well.

@fairlighteth
fairlighteth self-requested a review June 2, 2026 08:11

@fairlighteth fairlighteth 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.

✅ Manual verification: recipient URL and reset flows pass
  • Tested /#/1/swap/WETH/COW?recipient=vitalik.eth: recipient stays prefilled after load and wallet connect.
  • Tested no-recipient URL: recipient stays empty.
  • Tested manual recipient plus wallet/network chain switch: recipient is cleared.
  • Tested EVM recipient, cleared buy token, then selected BTC/SOL output: incompatible recipient is cleared.
  • Result: all checks passed.
✅ AI Review (Codex GPT-5, worked 12m): no new findings

Review completed. I found no new non-duplicate comments worth posting.

Related context checked

  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipientOnChainChange.ts: @fairlighteth's prior P2 is addressed.
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts: CodeRabbit/@fairlighteth's unused isNativeIn dependency note is fixed.
  • apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts: the original ?recipient=vitalik.eth flow is now guarded synchronously from useLocation().search.

@vercel
vercel Bot temporarily deployed to Preview – explorer-dev June 5, 2026 10:58 Inactive
@vercel
vercel Bot temporarily deployed to Preview – swap-dev June 5, 2026 10:59 Inactive
@fairlighteth

Copy link
Copy Markdown
Contributor

@tenderdeve there's a merge conflict now. Could you review and resolve this? If all good we'd happy to merge.

…-recipient-from-url

# Conflicts:
#	apps/cowswap-frontend/src/modules/trade/hooks/useResetRecipient.ts
@tenderdeve

Copy link
Copy Markdown
Contributor Author

@tenderdeve there's a merge conflict now. Could you review and resolve this? If all good we'd happy to merge.

@fairlighteth Resolved conflicts.

@alfetopito
alfetopito merged commit c5ef87c into cowprotocol:develop Jun 8, 2026
10 of 18 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom link: recipient is not prefilled into the form

4 participants