Skip to content

wasm: add walletdk browser demo - #431

Closed
sputn1ck wants to merge 4 commits into
mainfrom
feat/wasm-wallet-demo
Closed

wasm: add walletdk browser demo#431
sputn1ck wants to merge 4 commits into
mainfrom
feat/wasm-wallet-demo

Conversation

@sputn1ck

Copy link
Copy Markdown
Member

Summary

Adds the walletdk browser demo implementation branch:

  • OPFS-backed SQLite for daemon, swap, and lwwallet wallet DB paths in WASM
  • grpc-gateway browser adapters for Ark, mailbox, and swap RPC seams
  • static walletdk demo UI with create, unlock, balances, address, receive, send, swaps, and activity surfaces
  • GitHub Pages build/deploy workflow
  • committed Playwright smoke harness for Chromium create, address, reload, unlock, identity persistence, swapruntime startup, and OPFS verification

Validation

  • make wasm-wallet-demo-browser-test
  • make unit pkg=./db timeout=5m
  • make unit pkg=./sdk/swaps timeout=5m
  • make unit pkg=./sdk/walletdk timeout=5m
  • env GOOS=js GOARCH=wasm go test -c -o /private/tmp/db.test.wasm ./db
  • env GOOS=js GOARCH=wasm go test -c -o /private/tmp/swaps.test.wasm ./sdk/swaps
  • env GOOS=js GOARCH=wasm go test -c -tags swapruntime -o /private/tmp/walletdk.test.wasm ./sdk/walletdk
  • env GOOS=js GOARCH=wasm go build -tags swapruntime -o /private/tmp/walletdk.wasm ./cmd/walletdk-wasm
  • make commitmsg-lint range=90888222..HEAD

Remaining follow-up

This is a draft because Playwright MCP was not exposed in the local session, the Pages workflow has not run from main, and full receive/send behavior still needs validation against a live SwapDK test instance.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces walletdk, a wallet-facing SDK that provides a stable API over an embedded darepod daemon, supporting both native and browser-based environments. Significant additions include a WebAssembly bridge, a web demo utilizing OPFS-backed SQLite, and gRPC-gateway support for HTTP/JSON communication. The review identified high-severity bugs in the WASM implementation where satoshi amounts could be truncated due to 32-bit integer limits, as well as a medium-severity XSS risk in the web demo's UI rendering. Additionally, the reviewer recommended using limited readers for gateway responses to mitigate potential memory exhaustion from oversized payloads.

Comment thread cmd/walletdk-wasm/main.go Outdated
}

return client.Receive(ctx, walletdk.ReceiveRequest{
AmountSat: int64(req.Get("amountSat").Int()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In Go WASM builds, syscall/js.Value.Int() returns a 32-bit integer. Satoshi amounts can easily exceed the 32-bit range (approx 21.47 BTC), which will result in silent truncation for large values. Since JavaScript numbers are 64-bit floats, they can safely represent integers up to $2^{53}-1$.

Suggested change
AmountSat: int64(req.Get("amountSat").Int()),
AmountSat: int64(req.Get("amountSat").Float()),

Comment thread cmd/walletdk-wasm/main.go Outdated

return client.Send(ctx, walletdk.SendRequest{
Invoice: stringValue(req.Get("invoice")),
MaxFeeSat: uint64(req.Get("maxFeeSat").Int()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using Int() for maxFeeSat will cause truncation for values exceeding $2^{31}-1$. Use Float() to correctly capture the full range of the value from JavaScript.

Suggested change
MaxFeeSat: uint64(req.Get("maxFeeSat").Int()),
MaxFeeSat: uint64(req.Get("maxFeeSat").Float()),

Comment thread web/walletdk-demo/src/app.js Outdated
body.replaceChildren();
for (const swap of swaps || []) {
const row = document.createElement("tr");
row.innerHTML = `<td>${swap.Direction}</td><td>${swap.State}</td><td>${swap.AmountSat}</td><td>${swap.PaymentHash}</td>`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

Using innerHTML to render swap data is a security risk as it can lead to Cross-Site Scripting (XSS) if the data contains malicious HTML. Use textContent for each cell to ensure safe rendering.

    [swap.Direction, swap.State, swap.AmountSat, swap.PaymentHash].forEach(text => {
      const cell = document.createElement("td");
      cell.textContent = text;
      row.appendChild(cell);
    });

Comment thread arkrpc/gateway_client.go Outdated
}
defer httpResp.Body.Close()

respBody, err := io.ReadAll(httpResp.Body)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

io.ReadAll is used without a limit on the response body. This could lead to high memory usage or a crash if the gateway returns an excessively large payload. Consider using io.LimitReader to bound the input size.

Comment thread rpc/swapclientrpc/gateway_client.go Outdated
defer httpResp.Body.Close()

respBody, err := io.ReadAll(httpResp.Body)
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The response body is read entirely into memory using io.ReadAll without a size limit. To prevent potential memory exhaustion from large responses, use io.LimitReader with a sensible maximum size.

Comment thread serverconn/gateway_mailbox_client.go Outdated
defer httpResp.Body.Close()

respBody, err := io.ReadAll(httpResp.Body)
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Reading the entire response body with io.ReadAll without a limit is risky. Use io.LimitReader to protect against memory exhaustion from unexpectedly large responses.

@sputn1ck
sputn1ck force-pushed the feat/wasm-wallet-demo branch 9 times, most recently from ec77b9f to 1dc3883 Compare May 14, 2026 18:47
@sputn1ck
sputn1ck force-pushed the feat/wasm-wallet-demo branch from 1dc3883 to de06b74 Compare May 19, 2026 18:06
ellemouton pushed a commit that referenced this pull request May 22, 2026
itest: cut wall-clock on the suite's slowest tests by ~60%
@sputn1ck
sputn1ck force-pushed the feat/wasm-wallet-demo branch from de06b74 to 04d0d1c Compare May 23, 2026 15:08
sputn1ck added 4 commits May 25, 2026 08:27
Add wasm-safe SQLite open, migrate, and error handling through
go-wasmsqlite. Browser builds use OPFS while native builds keep the
existing database/sql path.

Move lwwallet walletdb and seed storage behind native and wasm files so
browser wallets can persist state in OPFS.
Expose the embedded walletdk runtime through a js/wasm command callable
from browser code. The bridge wraps wallet, swap, and raw RPC methods
without a separate gateway process.

Add wasm Ark transport and inline OOR actor wiring for the single
browser process.
Add a React workspace with core, wasm-web, and react packages. The UI
starts the wasm runtime, creates or unlocks the OPFS wallet, and exposes
receive, send, and activity flows matching the TUI demo.

Include local build, serve, and Playwright smoke targets for reviewers.
Add a GitHub Pages workflow that builds the wasm wallet demo and
publishes the static bundle with the isolation headers needed for OPFS
and SharedArrayBuffer support.
@Roasbeef

Copy link
Copy Markdown
Member

Superseded by #803, which rebuilds the wasm wallet bindings against current main. This branch had gone stale: the OOR actor set was rewritten (the global signing-effect actor that the inline OOR path wrapped is gone), the walletdk.Client send path moved to a prepare/dispatch split, and sdk/walletdk was reorganized around the walletdkrpc rename.

The new PR drops the inline OOR actor entirely (the durable registry + per-session actors run under js/wasm as-is), drives the browser bridge through the sdk/walletdk/mobile JSON facade so it can't drift from the Client API, and leaves out the app-specific bits (React demo, Playwright, Pages workflow). Let's move review over to #803.

cc @jamaljsr

@Roasbeef Roasbeef closed this Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants