Skip to content

fix: validate withdrawal address in watcher (#13)#23

Open
odiseusme wants to merge 1 commit into
rosen-bridge:devfrom
odiseusme:fix/issue-13-validate-withdrawal-address
Open

fix: validate withdrawal address in watcher (#13)#23
odiseusme wants to merge 1 commit into
rosen-bridge:devfrom
odiseusme:fix/issue-13-validate-withdrawal-address

Conversation

@odiseusme

Copy link
Copy Markdown

Fixes #13.

Changes

  • Adds ergo-lib-wasm-browser (^0.24.1) to apps/watcher dependencies
  • Adds webpack/WASM configuration to apps/watcher/next.config.js (mirrors the experiments block from apps/rosen/next.config.js, plus output.webassemblyModuleFilename needed for output: 'export')
  • Adds apps/watcher/src/utils/validateErgoAddress.ts — a browser-side Ergo address validator that lazy-loads the WASM module on first use
  • Refactors the address field in apps/watcher/src/app/actions/@form/withdraw/page.tsx from register('address', { required }) to useController with an async validate rule, mirroring the pattern in apps/rosen/src/hooks/useBridgeForm.ts

Validation behavior

The validator uses Address.from_base58() from ergo-lib-wasm-browser, which accepts both mainnet and testnet Ergo addresses. The issue specifies "an Ergo address" (not "a mainnet Ergo address"), and a Watcher operator may be running a testnet deployment — restricting to mainnet would break testnet operators.

Error messages mirror the Rosen app's useBridgeForm exactly:

  • Empty input: "Address cannot be empty" (intentional change from the previous "Address is required" to match Rosen)
  • Invalid address: "Invalid Address"
  • Unexpected error during validation: "Something went wrong! please try again"

Verification

  • npm run build for apps/watcher succeeds; the .wasm chunk is emitted to out/_next/static/wasm/
  • The WASM file is served with Content-Type: application/wasm and HTTP 200 from a static-server smoke test
  • No new TypeScript errors introduced (verified by comparing npm run type-check output against the pre-PR baseline)

Notes for reviewers

@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

@odiseusme is attempting to deploy a commit to the rosen-bridge Team on Vercel.

A member of the Team first needs to authorize it.

@odiseusme

Copy link
Copy Markdown
Author

Verification of acceptance conditions (done locally via source inspection):

  1. Error display: Uses react-hook-form's formState.errors.address.message rendered through the shared TextField's helperText prop (same @rosen-bridge/ui-kit component as Rosen). Visual parity follows from the shared component.

  2. Validation logic: Equivalent to Rosen's by construction. Rosen's chain runs server-side: useBridgeForm → Server Action → @rosen-network/base.validateAddress@rosen-bridge/address-codec → for ERGO_CHAIN: ergoLib.Address.from_base58(address). Our PR runs Address.from_base58(address) directly in the browser via ergo-lib-wasm-browser. Same Rust source, same accept/reject behavior. The Watcher's output: 'export' precludes Server Actions, which is why we go direct-to-WASM.

  3. react-hook-form API: useController with async validate rule, mirroring apps/rosen/src/hooks/useBridgeForm.ts. Empty check ("Address cannot be empty"), validation call, fallback ("Invalid Address"), catch-all ("Something went wrong! please try again") — all identical strings to Rosen's flow.

One small UX delta: we trim whitespace on the input's onBlur before validation. Rosen's ErgoNetwork.toSafeAddress is a no-op pass-through (return address), so where Rosen would reject " 9fuoH... ", our form accepts it. We've kept this small UX improvement intentionally — happy to discuss if there's a reason to revert.

@abdolian

Copy link
Copy Markdown
Collaborator

I reviewed your #23 merge request. It works, but it’s not efficient.

Loading ergo-lib-wasm-browser on the client adds unnecessary overhead. We prefer handling this in the API and returning a simple validation result to the browser.

We’re planning to implement this on the API side, so we can continue this merge after that change.

@odiseusme

Copy link
Copy Markdown
Author

I’m happy to take the API-side implementation on as part of this PR if that’s useful — a validation endpoint returning a simple per-chain result, with the form wired to call it instead of running WASM in the browser. Or if you’d rather build the primitive internally, I’ll wait and adapt the client once it lands. Whichever fits your roadmap — just let me know.

@abdolian

abdolian commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

We will implement the API first, and then you can continue.

@odiseusme

Copy link
Copy Markdown
Author

Sounds good — I'll wait for the API-side validation to land, then refactor this PR to drop the client-side WASM and call the endpoint instead. Ping me here (or link the endpoint) once it's ready and I'll pick it back up.

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.

Validate withdrawal address in Watcher

2 participants