0xMiden / 0xMiden/web-sdk

React SDK: subscription primitive for account-state updates driven by external consumers

Open
#107 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1
Forks
21
Avg merge
12h 14m
Merged PRs (30d)
41

Description

## Context

`@miden-sdk/react` exposes [`useWaitForCommit`](https://github.com/0xMiden/miden-client) to wait on transactions submitted *by the local client*. There is no equivalent hook for the case where:

- The local WebClient **tracks** an account (network-mode or public), but
- A state change is driven by a transaction the **local client never submitted or saw** — e.g. a note consumed by the network operator, or a note consumed by a different client whose effect we care about.

A concrete example lives in the [Miden frontend template](https://github.com/0xMiden/frontend-template) — `src/hooks/useIncrementCounter.ts`:

1. The user submits an increment note via the MidenFi wallet (so the local WebClient never gets the tx into its log).
2. The network operator picks up the note (`NoteAttachment::newNetworkAccountTarget`) and executes it against the counter account.
3. The frontend needs to observe the counter's storage-map value changing.

Because step 1 bypasses the local client and step 2 happens remotely, `useWaitForCommit` cannot help. The template currently works around this with a fixed-interval poll (2.5 s, 30 s timeout) that calls `client.syncState()` + reads the storage map in a loop, which is wasteful and bounded by an arbitrary timeout.

## Proposal

Add a React-SDK-level primitive along the lines of:

```ts
// Resolves when the account's digest (or a specified storage slot/map entry) changes
// past the given baseline, or rejects on timeout/unsubscribe.
const { promise, unsubscribe } = useWaitForAccountUpdate(accountId, {
baselineDigest, // optional: what we consider \"old\"
slot, // optional: narrow to a specific storage slot / map key
timeoutMs,
});
```

or a lower-level observable:

```ts
const stream = client.subscribeAccountUpdates(accountId);
for await (const update of stream) { ... }
```

Implementation could be internal to the WebClient — e.g. a Dexie observable on the local store (as suggested by @evanmarshall in 0xMiden/miden-client#467), or a push-based subscription once [`#467`](https://github.com/0xMiden/miden-client/issues/467) lands an event-listener framework in the Rust core.

## Relation to 0xMiden/miden-client#467

[#467](https://github.com/0xMiden/miden-client/issues/467) tracks the general \"allow users to react to events\" design in the Rust client. This issue is narrower and targeted at the **React / WebClient** layer: exposing an idiomatic hook/API for the frontend use case above. Depending on scoping, this can either:

- Land as a thin wrapper over whatever 0xMiden/miden-client#467 produces, or
- Ship independently as a WebClient-only primitive (e.g. via Dexie observables on the IndexedDB store), since the WebClient has specific ergonomics and need not wait on the full Rust-side redesign.

## Acceptance criteria

- Frontend code no longer needs a fixed-interval poll loop to observe externally-driven account-state updates.
- The primitive correctly fires when either (a) the network operator consumes a note targeting the tracked account, or (b) any other external client submits a tx that updates the tracked account.
- Cleanly cancellable (component unmount, route change) without leaking timers/listeners.

## Workaround reference

For context, the workaround code + inline rationale:
https://github.com/0xMiden/frontend-template/blob/main/src/hooks/useIncrementCounter.ts

Contributor guide

Open the contributing guide

Research direction

Look at the existing `useWaitForCommit` hook in the React SDK and the WebClient's internal store (likely using Dexie/IndexedDB). Examine the workaround in the frontend-template's `useIncrementCounter.ts`. The goal is to create a new hook or subscription primitive that observes external account-state changes. Start by understanding how the client tracks accounts and syncs state (`client.syncState()`). 'Done' means the frontend template can replace its polling loop with the new primitive.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, react, typescript
Domain
api, frontend, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.