Skip to content

fix(react-sdk): rebuild by-value WASM handles per poll/refetch iteration - #296

Open
emreyurur wants to merge 1 commit into
0xMiden:mainfrom
emreyurur:fix/wasm-by-value-reuse
Open

fix(react-sdk): rebuild by-value WASM handles per poll/refetch iteration#296
emreyurur wants to merge 1 commit into
0xMiden:mainfrom
emreyurur:fix/wasm-by-value-reuse

Conversation

@emreyurur

Copy link
Copy Markdown

Summary

Part of #278.

getConsumableNotes takes Option<AccountId> and TransactionFilter::ids takes Vec<TransactionId> by value, so wasm-bindgen calls __destroy_into_raw() and zeroes the JS wrapper's pointer on the first call. Four hooks kept one such object in a variable and handed it over again on later iterations, throwing null pointer passed to rust on every use after the first.

The fix everywhere is the shape #270 established: snapshot the stable value (a hex string, or the original account reference) once, and rebuild a fresh handle per iteration. It's also the convention the package already follows elsewhere — useMultiSend rebuilds iterSenderId / iterAssetId per iteration and useSessionAccount rebuilds freshAccountId.

Changes

  • useWaitForCommit.tsTransactionFilter.ids([txId]) consumed the caller's TransactionId on the first poll. targetHex was already snapshotted, so the id is now rebuilt from it each iteration.
  • useTransactionHistory.ts — the memoised TransactionIds were handed to TransactionFilter.ids again on every refetch, so the second refetch (e.g. the one a sync tick drives) threw. Ids are now rebuilt from the existing idsHex snapshot.
  • useTransaction.tstxId.toHex() was read after waitForTransactionCommit had consumed the handle. The hex is now snapshotted before the call.
  • useWaitForNotes.ts — not in React SDK reuses by-value WASM objects across poll/refetch iterations ("null pointer passed to rust") #278's list; found while tracing the others. A single AccountId was built before the loop and passed to getConsumableNotes on every poll, so waitForConsumableNotes only ever worked when the notes were already present and failed whenever it actually had to wait. A fresh AccountId is built per poll; the reference is still parsed once up front so a malformed id rejects before the first sync, exactly as before.
  • CHANGELOG.md[FIX][react] entry under Unreleased.

Not included

Testing

New packages/react-sdk/src/__tests__/hooks/issue278-wasm-by-value-reuse.test.tsx, plus a case added to useWaitForNotes.test.tsx.

The existing suite couldn't catch any of this: setup.ts mocks TransactionFilter.ids as a plain object factory and models none of wasm-bindgen's move semantics, so a consumed handle stays perfectly usable in tests. The new tests install a mock that does model it — marking each id consumed and throwing null pointer passed to rust on reuse, mirroring how setup.ts already does this for sendPrivateNote.

Before the fix, against main:

× #278 > useTransactionHistory rebuilds TransactionIds on every refetch
× #278 > useWaitForCommit rebuilds the TransactionId on every poll
  → Error: null pointer passed to rust
  ❯ src/hooks/useWaitForCommit.ts:53:33
× useWaitForNotes > rebuilds the AccountId per poll ...
  → Error: null pointer passed to rust
  ❯ src/hooks/useWaitForNotes.ts:42:50

After: full package suite green — 63 files, 848 tests (vitest run). ESLint clean on every changed file.

Two existing assertions had to be updated: useWaitForCommit.test.tsx and useTransactionHistory.test.tsx asserted that TransactionFilter.ids was called with the caller's exact object, which pinned the buggy behaviour. They now assert on the id's hex instead of object identity.

useTransaction.ts has no dedicated regression test — its change is a pure snapshot-before-use and the existing useTransaction suite covers the surrounding flow. Happy to add a targeted one if you'd like it.

Verified in a clean fork clone with a full pnpm install, not just in isolation.

getConsumableNotes and TransactionFilter.ids take their arguments by value, so wasm-bindgen moves the handle into WASM and zeroes the JS wrapper's pointer. Four hooks held one such object across poll/refetch iterations and threw 'null pointer passed to rust' on every use after the first.

Part of 0xMiden#278.
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