What this does
Adds crypto deposits with three paths, built on Fireblocks custody.
| Path | Status |
|---|---|
| Send from a connected wallet — Reown AppKit + wagmi, EVM chains | working |
| Send from an exchange | disabled, shown as "Coming soon" |
| On-chain deposit to an address — user's Fireblocks vault | working |
Supporting backend work: Fireblocks webhook crediting, deposit intent tracking and matching, a cron that cancels abandoned intents, network/asset catalog sync, and the deposit status/submitted endpoints.
Why the exchange path ships disabled
Daimo was the provider for that path and has been removed entirely — backend
module, webhook events table and migration, config, the exchange_daimo
payment method, and the frontend webview and session hook. The path stays on
the deposit method screen behind a "Coming soon" badge until a replacement
provider is chosen.
Fixes from a review pass over the branch
-
A stale response could show the wrong deposit address. The detail screen
fired an address request per network/token change with no ordering guard, so
switching network while one was in flight let the abandoned response land
last and overwrite the address on screen — a user could copy an address for
one chain and send funds on another. Requests now carry a sequence number and
only the newest may write. The screen also had no
onError, so a failed request left a blank address with no message; it now shows the failure and a retry. -
isAssetWalletAlreadyExistsErrormatched a barealready exists, which also catches unrelated Fireblocks rejections — swallowing one returns a deposit address for a create that actually failed. Now matches the asset wallet message specifically. -
markSubmittedskipped the whole write on an already-COMPLETED deposit, so a webhook confirming before the client reported its hash left the deposit with notxHashorsourceAddressfor good. The terminal-status guard now coverspaymentStatusonly.
Each has a test that fails without the fix.
-
REOWN_PROJECT_IDwas never committed. It was duplicated across the four per-environment.envfiles but tracked in none of them, andenv.jsrequires it — so a fresh clone or an EAS cloud build failed at startup. It now lives in the committedfe/.env.shared, loaded after.env.<APP_ENV>so an environment can still override it.
Verification
| Result | |
|---|---|
| FE tests | 41/41 pass, 7/7 suites |
| FE type-check | clean |
| FE lint | clean on changed files |
| BE build | passes |
| BE lint | clean on changed files |
| BE tests | 108 pass / 23 fail |
Known issues, please read before merging
23 backend tests fail across 14 suites. These are pre-existing on this branch, not caused by the changes above — verified by stashing them and re-running: the failing list is identical, only the pass count differs (105 → 108). They are dependency-injection failures in cards, chats, contacts, kyc, loyalties, notifications, reap and users, left over from the daimo removal. Not fixed here. If CI gates on tests this needs a follow-up.
An already-applied migration was reformatted.
1764610000000-seed-admin-roles-permissions.ts gained +173/-60 lines of pure
reflow. The generated SQL is identical, so environments that already ran it are
unaffected — flagging it because editing an applied migration normally warrants
a second look.
Not covered
Review also raised the intent-matching window (userId + asset + 1 hour) as
able to attach a credited deposit to the wrong intent when a user starts two
deposits for the same asset close together. Left as-is: findByTxHash runs
first as the precise discriminator, the service writes metadata only, and the
amount is taken from the chain — so a mismatch mislabels a record, it does not
misplace money. Worth a deterministic reference later.