Skip to content

feat(withdraw): withdraw soUSD on Base - #2675

Open
MayankMittal1 wants to merge 4 commits into
masterfrom
mayank/feat/sousd-base-withdraw
Open

MayankMittal1 wants to merge 4 commits into
masterfrom
mayank/feat/sousd-base-withdraw

Conversation

@MayankMittal1

@MayankMittal1 MayankMittal1 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Lets soUSD withdrawals run through Base instead of Ethereum, behind EXPO_PUBLIC_SOUSD_BASE_WITHDRAWALS. It is off by default, so merging changes nothing until the flag is set.

With the flag on

  • Step 1: bridges soUSD from Fuse to Base (EID 30184) through the Fuse BridgePaymaster.
  • Step 2: queues the shares on the Base queue (0x204b…21e2) for Base USDC. The copy reads "Bridge to Base" and "USDC on Base".
  • Already on Ethereum: shares already bridged there still finish on Ethereum. That includes resumed withdrawals that older builds saved, which have no chain recorded. Cancel uses the chain the request was made on.

Always on (production config)

  • Base vault addresses: the vault, teller, accountant and queue on Base are in ADDRESSES.base. QA has no Base contracts, so QA builds stay on Ethereum unless the EXPO_PUBLIC_BASE_SOUSD_* overrides are set.
  • soUSD on Base in savings:
    • it shows in the wallet and merges into the soUSD coin
    • it is priced at the accountant rate
    • it counts toward the savings balance, not wallet cash
  • Base withdraw status, read on-chain: Base has no subgraph, so the status comes from the queue.
    • Processing while the request ID is in getRequestIds(), re-checked every minute.
    • Success once it leaves the queue, or Cancelled if the user cancelled it in the app.
    • Known gap: a request the cron cancels after its deadline also shows as Success, because the queue doesn't record which way a request left.
    • A queued Base request can be cancelled from its activity screen.
  • Savings deposit-fee notice: it now asks the backend about soUSD on every chain, because only the backend knows whether soUSD deposits are on Ethereum or Base.

Rollout

Turn the flag on only after the backend's Base solver doc is in place and the Base vault holds a USDC buffer. Until then, Base requests would never be solved, or the solve would revert. The contracts side is live: the Fuse teller is peered with Base, the vault's transfer hook is set, and the roles are configured.

Testing

  • Fork simulation of the full flow on anvil forks of Base and Fuse against the live contracts: bridge, queue request, solve, user cancel and cron cancel. The status read returned the right value at each step.
  • Jest, one file at a time:
    • useWithdraw, soUsdWithdrawStatus (new), depositFee
    • useActivity.freshness (adds four Base status cases)
    • portfolio, vaultWalletBalance, AssetsOverview, savingsFundTokenGroups
    • alchemyTokenPrices, cardSpendable, withRefreshToken, three BuyFuse suites
    • all passing
  • tsc --noEmit and eslint are clean on the changed files.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

MayankMittal1 and others added 4 commits October 8, 2026 15:39
…WITHDRAWALS

With the flag on, a withdrawal that starts on Fuse bridges its shares to
Base and queues them on the Base queue for Base USDC. Shares already on
Ethereum, and resumed withdrawals that older builds bridged there, still
finish on Ethereum; cancel uses the request's own chain.

Base soUSD now shows in the wallet and counts toward the savings balance.
The savings deposit-fee notice asks the backend about soUSD on every chain,
since only the backend knows whether soUSD deposits are on Ethereum or Base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base has no withdraw subgraph, so a Base withdraw request showed as
Processing forever. Read it from the Base queue instead: pending while its
id is in the queue's outstanding set, then solved or cancelled from the
event that removed it. Pending requests are re-read every minute; finished
ones are never re-read. Unknown stays Processing rather than guessing.

The request id is attached to the activity, so a queued Base withdraw can
be cancelled from its activity screen, and a cancel re-reads the status at
once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read only the Base queue's outstanding set: a request still in it is
Processing, and once it leaves it is Success - or Cancelled when the user
cancelled it in the app. Drops the eth_getLogs lookup. A request the solver
cancels after its deadline has no cancel activity, so it now shows as
Success.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
soUSD on Base (shares between a withdrawal's bridge and its queue
request) now links to the same soUSD coin as on Ethereum and Fuse: it
takes the curated soUSD list entry, and the Base vault joins the
production shares a dev wallet recognises.

Tests: the activity freshness suite mocks the Base withdraw hook and
covers the Base statuses; the status test names its mock client as jest
requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated
solid-app Ignored Ignored Oct 8, 2026 3:37pm UTC
solid-app-staging Ignored Ignored Oct 8, 2026 3:37pm UTC

Request Review

@claude

claude Bot commented Oct 8, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

This branch has not been deployed

No deployments
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