Skip to content

Escrow contract switch#554

Open
bogdanfazakas wants to merge 2 commits into
mainfrom
feat/contract-dropdown
Open

Escrow contract switch#554
bogdanfazakas wants to merge 2 commits into
mainfrom
feat/contract-dropdown

Conversation

@bogdanfazakas

@bogdanfazakas bogdanfazakas commented Jul 2, 2026

Copy link
Copy Markdown
Member

Fixes #552 .

Changes proposed in this PR:

  • Adds an optional NEXT_PUBLIC_LEGACY_ESCROW_ADDRESS env variable. When set and only on Base the escrow page (/profile/escrow) shows an Escrow contract dropdown with Current contract
  • Selecting Legacy switches the whole page to the old deployment: available/locked balances, active locks, and authorizations are read from the legacy contract, and Withdraw sends the transaction to it for both EOA (MetaMask/ethers signer) and smart-account (Alchemy/viem) wallets.
  • Legacy mode is view + withdraw only: the deposit form is replaced with a hint, and authorization Create/Edit/Revoke are hidden (cards render read-only). A "Withdraw only" chip and a hint under the selector make the mode obvious.
  • When the env var is unset or invalid, or on Sepolia, the selector is hidden and the page behaves exactly as before. The run-job payment flow and the node-details withdraw modal are untouched and always use the current contract.

Summary by CodeRabbit

  • New Features

    • Added support for viewing and using a legacy escrow contract alongside the current one.
    • Added a homepage banner that can link users to the legacy escrow flow and can be dismissed.
  • Bug Fixes

    • Improved escrow data loading when switching between contract views.
    • Updated withdrawals and authorization details to work correctly with the selected contract.
    • Legacy contract mode now clearly limits actions like deposits and authorization changes to withdraw-only behavior.

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
nodes-dashboard Ready Ready Preview, Comment Jul 3, 2026 12:07pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a legacy escrow contract selector allowing users to view balances/locks and withdraw funds from a previous escrow deployment via an env-configured address, while restricting deposits and authorization actions in legacy mode. Also adds a dismissible homepage banner linking to the legacy escrow flow.

Changes

Legacy Escrow Contract Selector

Layer / File(s) Summary
Legacy escrow constant and env docs
README.md, src/constants/escrow.ts
Documents NEXT_PUBLIC_LEGACY_ESCROW_ADDRESS and adds EscrowContractVersion type plus LEGACY_ESCROW_ADDRESS, valid only on Base chain with a valid address.
OceanProvider escrow address override
src/lib/ocean-provider.ts
getEscrowContract and getAllAuthorizations, getUserFundsDetailed, getLocks, withdrawTokensEoa accept an optional escrowAddress to target an alternate escrow deployment.
useEscrowData contract-aware loading
src/lib/use-escrow-data.ts
Hook accepts escrowAddress, adds race-safe load tracking, passes contractAddress into provider calls, filters locks client-side, and reloads on address change.
useWithdrawTokens escrow target resolution
src/lib/use-withdraw-tokens.ts
Adds escrowAddress to withdraw params; EOA and non-EOA withdrawal paths resolve and target the correct escrow contract.
Escrow page contract selector UI
src/components/escrow/escrow-page.tsx, src/components/escrow/escrow-page.module.css
Adds a Select for switching between current/legacy contracts, legacy hint text, a "Withdraw only" chip, and passes escrowAddress to token panels.
EscrowTokenPanel legacy-mode restrictions
src/components/escrow/escrow-token-panel.tsx, src/components/escrow/escrow-token-panel.module.css
Disables deposits and authorization creation/edit/revoke in legacy mode, forwards escrowAddress for withdrawal, and adjusts empty-state messaging.
Homepage legacy escrow banner
src/components/homepage/hero-section.tsx, src/components/homepage/legacy-escrow-banner.tsx, src/components/homepage/legacy-escrow-banner.module.css
Adds a dismissible banner shown when a legacy escrow address is configured, linking to the escrow management page.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EscrowPage
  participant useEscrowData
  participant OceanProvider
  participant EscrowContract

  User->>EscrowPage: select "Legacy contract"
  EscrowPage->>EscrowPage: compute escrowAddress from LEGACY_ESCROW_ADDRESS
  EscrowPage->>useEscrowData: useEscrowData(escrowAddress)
  useEscrowData->>OceanProvider: getUserFundsDetailed(token, address, escrowAddress)
  useEscrowData->>OceanProvider: getAllAuthorizations(token, payer, escrowAddress)
  useEscrowData->>OceanProvider: getLocks(token, payer, payee, escrowAddress)
  OceanProvider->>EscrowContract: query balances/locks/authorizations at escrowAddress
  EscrowContract-->>OceanProvider: data
  OceanProvider-->>useEscrowData: tokens/spenders/locks
  useEscrowData-->>EscrowPage: updated state
  EscrowPage-->>User: renders read-only authorizations, withdraw-only actions

  User->>EscrowPage: submit withdraw
  EscrowPage->>useEscrowData: handleWithdraw({..., escrowAddress})
  useEscrowData->>OceanProvider: withdrawTokensEoa({tokenAddresses, amounts, escrowAddress})
  OceanProvider->>EscrowContract: withdraw from escrowAddress
  EscrowContract-->>OceanProvider: tx result
  OceanProvider-->>EscrowPage: withdrawal confirmed
Loading

Possibly related PRs

  • oceanprotocol/nodes-dashboard#518: Both PRs extend the same escrow data/provider logic (use-escrow-data.ts, ocean-provider.ts) and EscrowTokenPanel component to parameterize contract selection.

Suggested reviewers: mihaisc, dnsi0, ndrpp, giurgiur99

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The new homepage legacy-escrow banner and hero-section integration are unrelated to the escrow-page selector work. Move the homepage banner into a separate PR unless it is explicitly required for the escrow selector feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main escrow contract switching change.
Linked Issues check ✅ Passed The PR adds the escrow selector, legacy contract reads and withdrawals, and hides deposit/authorization actions in legacy mode as requested.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/contract-dropdown

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@giurgiur99

Copy link
Copy Markdown
Contributor

/run-security-scan

@alexcos20 alexcos20 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

AI automated code review (Gemini 3).

Overall risk: low

Summary:
This PR is exceptionally well-implemented. It handles the legacy escrow contract redeployment seamlessly by adding a fallback selector and safe read-only/withdraw modes. The approach used to handle React race conditions during data fetching is highly commendable. All edge cases regarding disabled deposits and authorizations on the legacy contract have been thoroughly accounted for.

Comments:
• [INFO][bug] It is a good practice to wrap localStorage access in a try...catch block. In some environments (like Safari Private Browsing or restricted iframes), accessing localStorage throws a SecurityError or QuotaExceededError, which could cause the React component to crash during mount or user interaction.

Consider wrapping the logic safely:

-    setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    try {
+      setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    } catch (err) {
+      setVisible(true);
+    }

And in your dismiss handler:

-    localStorage.setItem(DISMISSED_KEY, 'true');
-    setVisible(false);
+    try {
+      localStorage.setItem(DISMISSED_KEY, 'true');
+    } catch (err) {
+      // ignore
+    }
+    setVisible(false);

• [INFO][style] The way you handled race conditions with loadIdRef and escrowAddressRef is excellent! Ensuring that late-arriving data from an old contract fetch doesn't overwrite the current state is a very robust pattern for data fetching in React.
• [INFO][other] Great job on preventing deposits to the legacy contract right at the UI level while keeping the hook usage clean.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/escrow/escrow-token-panel.tsx (1)

220-254: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Withdraw/deposit form values persist across contract switch.

EscrowTokenPanel isn't remounted when escrowAddress changes (no key change), and there's no effect resetting depositForm/withdrawForm on that change. A previously-entered amount from the current contract's view carries over when switching to the legacy view (or back), which can confuse users even though submission still correctly targets the resolved escrowAddress.

💡 Proposed fix: reset forms when escrowAddress changes
 const EscrowTokenPanel = ({ token, spenders, loadingSpenders, onChange, escrowAddress }: EscrowTokenPanelProps) => {
   const [isCreateOpen, setIsCreateOpen] = useState(false);
   const isLegacy = !!escrowAddress;
+
+  useEffect(() => {
+    depositForm.resetForm();
+    withdrawForm.resetForm();
+  }, [escrowAddress]);
🤖 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/components/escrow/escrow-token-panel.tsx` around lines 220 - 254, The
deposit and withdraw amounts in EscrowTokenPanel are persisting when
escrowAddress changes because the component is reused and the Formik state is
never reset. Add an effect in EscrowTokenPanel that watches escrowAddress and
calls both depositForm.resetForm() and withdrawForm.resetForm() whenever the
contract view switches, so the amount fields clear when moving between legacy
and current escrow views.
🧹 Nitpick comments (2)
src/components/homepage/legacy-escrow-banner.module.css (1)

8-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Magic-number coupling to hero-section.module.css breakpoints.

The negative margin-top values and breakpoints are hand-copied to mirror another file's layout, per the comment. Any future change to hero padding/breakpoints silently desyncs this file since there's no shared constant/variable enforcing the relationship.

🤖 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/components/homepage/legacy-escrow-banner.module.css` around lines 8 - 24,
The banner spacing in the legacy escrow CSS is hard-coupled to hero-section
layout values via copied breakpoints and negative margins. Refactor the .banner
rules to use shared design tokens or CSS custom properties sourced from the hero
layout, and update the `@media` thresholds to reference the same centralized
values so both files stay in sync. Use the existing .banner selector and the
hero-section.module.css breakpoint/padding definitions as the single source of
truth.
src/components/homepage/legacy-escrow-banner.tsx (1)

20-31: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Guard localStorage access against exceptions.

localStorage.getItem/setItem are called unguarded. In restrictive environments (e.g., private browsing modes, storage disabled by policy), these calls can throw and crash the component since there's no error boundary/try-catch here.

🛡️ Proposed defensive fix
   useEffect(() => {
-    setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    try {
+      setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    } catch {
+      setVisible(true);
+    }
   }, []);

   const dismiss = () => {
-    localStorage.setItem(DISMISSED_KEY, 'true');
-    setVisible(false);
+    try {
+      localStorage.setItem(DISMISSED_KEY, 'true');
+    } catch {
+      // ignore storage errors; still hide for this session
+    }
+    setVisible(false);
   };
🤖 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/components/homepage/legacy-escrow-banner.tsx` around lines 20 - 31, The
localStorage calls in LegacyEscrowBanner are unguarded and can throw in
restricted environments. Update the useEffect initializer and the dismiss
handler to wrap localStorage.getItem and localStorage.setItem in defensive
try/catch logic, and fall back to a safe default if storage is unavailable. Keep
the fix localized to LegacyEscrowBanner and its dismiss function so the
component still renders without crashing when storage access fails.
🤖 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.

Outside diff comments:
In `@src/components/escrow/escrow-token-panel.tsx`:
- Around line 220-254: The deposit and withdraw amounts in EscrowTokenPanel are
persisting when escrowAddress changes because the component is reused and the
Formik state is never reset. Add an effect in EscrowTokenPanel that watches
escrowAddress and calls both depositForm.resetForm() and
withdrawForm.resetForm() whenever the contract view switches, so the amount
fields clear when moving between legacy and current escrow views.

---

Nitpick comments:
In `@src/components/homepage/legacy-escrow-banner.module.css`:
- Around line 8-24: The banner spacing in the legacy escrow CSS is hard-coupled
to hero-section layout values via copied breakpoints and negative margins.
Refactor the .banner rules to use shared design tokens or CSS custom properties
sourced from the hero layout, and update the `@media` thresholds to reference the
same centralized values so both files stay in sync. Use the existing .banner
selector and the hero-section.module.css breakpoint/padding definitions as the
single source of truth.

In `@src/components/homepage/legacy-escrow-banner.tsx`:
- Around line 20-31: The localStorage calls in LegacyEscrowBanner are unguarded
and can throw in restricted environments. Update the useEffect initializer and
the dismiss handler to wrap localStorage.getItem and localStorage.setItem in
defensive try/catch logic, and fall back to a safe default if storage is
unavailable. Keep the fix localized to LegacyEscrowBanner and its dismiss
function so the component still renders without crashing when storage access
fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 729b9975-ff6d-4e15-b507-9bbf9e81ae74

📥 Commits

Reviewing files that changed from the base of the PR and between bb82fb7 and d61ab2c.

📒 Files selected for processing (12)
  • README.md
  • src/components/escrow/escrow-page.module.css
  • src/components/escrow/escrow-page.tsx
  • src/components/escrow/escrow-token-panel.module.css
  • src/components/escrow/escrow-token-panel.tsx
  • src/components/homepage/hero-section.tsx
  • src/components/homepage/legacy-escrow-banner.module.css
  • src/components/homepage/legacy-escrow-banner.tsx
  • src/constants/escrow.ts
  • src/lib/ocean-provider.ts
  • src/lib/use-escrow-data.ts
  • src/lib/use-withdraw-tokens.ts

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.

Escrow contract selector (legacy withdraw)

3 participants