You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In a real browser using PDK 1.0-rc.2 built via uniffi-bindgen-react-native 0.30.0-1 + wasm-bindgen --target web, both .save(persister) and .saveAsync(asyncPersister) on InitialSendTransition trap with RuntimeError: memory access out of bounds. Failure is at the FFI lift of the foreign callback — the persister's JS methods (save/load/close) are never invoked.
Everything that doesn't take a foreign callback works: PDK init, Uri.parse, checkPjSupported, new SenderBuilder(...), buildRecommended(...). Persister object shape is identical to the upstream InMemorySenderPersister[Async] test fixtures. Those fixtures pass under tsx --test (Node), but PDK's WASM bundle doesn't appear to be exercised in a real browser anywhere in CI — we may be the first browser exposure.
Related: #1389 (same pain on receiver-side validation), #1446 (open draft applying the architectural fix to receive-side), #1287 (added saveAsync with the apparent intent of unblocking WASM, but the async path lifts the same foreign handle and traps at the same point).
Stack trace (sync; async traps at the same lift)
RuntimeError: memory access out of bounds
at <Arc<Arc<dyn JsonSenderSessionPersister>> as Drop>::drop
at <dyn JsonSenderSessionPersister as FfiConverterArc>::try_lift
at uniffi_payjoin_ffi_fn_method_initialsendtransition_save::{closure#0}
Reading bottom-up: Rust enters save, try_lift on the foreign handle faults, and the partial Arc<Arc<...>> is unwound (the Drop frame).
Reproduction
import*asmodfrom'payjoin'awaitmod.uniffiInitAsync()constpdk=mod.default.payjoinconstpjUri=pdk.Uri.parse(BIP21_URI).checkPjSupported()// ✓constbuilder=newpdk.SenderBuilder(psbtBase64,pjUri)// ✓constinitial=builder.buildRecommended(feeRateSatPerVb)// ✓classMemSenderPersisterAsync{events=[]asyncsave(event){this.events.push(event)}asyncload(){returnthis.events}asyncclose(){}}awaitinitial.saveAsync(newMemSenderPersisterAsync())// RuntimeError: memory access out of bounds
The sync variant (new MemSenderPersister() + initial.save(...)) traps identically.
Environment
payjoin (PDK) — 1.0-rc.2
uniffi-bindgen-react-native — 0.30.0-1
wasm-bindgen-cli — 0.2.108
Build: upstream's payjoin-ffi/javascript, with ubrn.config.yaml patched from target: nodejs to target: web so wasm-bindgen emits a browser loader (the upstream web: block defaulting to nodejs looks like a separate bug — without the patch the emitted index.js does require('fs') and crashes immediately in any browser).
Vite 5 dev server, Chrome and Safari both reproduce.
uniffiInitAsync() is awaited before any FFI call; payjoin.default.initialize() runs and registers all callback vtables.
Ruled out
Wrong mod.payjoin shape (use mod.default.payjoin — works in both node and web entries).
GC of method-chain temporaries (named locals across awaits — no change).
Bare pj= vs full BIP 21 URI (parse already succeeds).
uniffiInitAsync() not awaited / vtables not registered (verified).
wasm-bindgen version drift (pinned to 0.2.108).
Persister method shape (matches upstream test fixtures verbatim).
Fix uniffi-bindgen-react-native's wasm-bindgen-target try_lift for foreign callback objects. Broader scope — likely fixes receiver validation callbacks in browsers too.
Happy to test candidate fixes against our repro and contribute a Playwright smoke test if that helps prevent regressions — javascript.yml currently builds the WASM bundle but doesn't browser-execute it.
Summary
In a real browser using PDK 1.0-rc.2 built via
uniffi-bindgen-react-native0.30.0-1 +wasm-bindgen --target web, both.save(persister)and.saveAsync(asyncPersister)onInitialSendTransitiontrap withRuntimeError: memory access out of bounds. Failure is at the FFI lift of the foreign callback — the persister's JS methods (save/load/close) are never invoked.Everything that doesn't take a foreign callback works: PDK init,
Uri.parse,checkPjSupported,new SenderBuilder(...),buildRecommended(...). Persister object shape is identical to the upstreamInMemorySenderPersister[Async]test fixtures. Those fixtures pass undertsx --test(Node), but PDK's WASM bundle doesn't appear to be exercised in a real browser anywhere in CI — we may be the first browser exposure.Related: #1389 (same pain on receiver-side validation), #1446 (open draft applying the architectural fix to receive-side), #1287 (added
saveAsyncwith the apparent intent of unblocking WASM, but the async path lifts the same foreign handle and traps at the same point).Stack trace (sync; async traps at the same lift)
Reading bottom-up: Rust enters
save,try_lifton the foreign handle faults, and the partialArc<Arc<...>>is unwound (theDropframe).Reproduction
The sync variant (
new MemSenderPersister()+initial.save(...)) traps identically.Environment
payjoin(PDK) —1.0-rc.2uniffi-bindgen-react-native—0.30.0-1wasm-bindgen-cli—0.2.108payjoin-ffi/javascript, withubrn.config.yamlpatched fromtarget: nodejstotarget: webso wasm-bindgen emits a browser loader (the upstreamweb:block defaulting tonodejslooks like a separate bug — without the patch the emittedindex.jsdoesrequire('fs')and crashes immediately in any browser).uniffiInitAsync()is awaited before any FFI call;payjoin.default.initialize()runs and registers all callback vtables.Ruled out
mod.payjoinshape (usemod.default.payjoin— works in both node and web entries).pj=vs full BIP 21 URI (parse already succeeds).uniffiInitAsync()not awaited / vtables not registered (verified).wasm-bindgenversion drift (pinned to 0.2.108).Suggested fix
Two paths:
uniffi-bindgen-react-native's wasm-bindgen-targettry_liftfor foreign callback objects. Broader scope — likely fixes receiver validation callbacks in browsers too.Happy to test candidate fixes against our repro and contribute a Playwright smoke test if that helps prevent regressions —
javascript.ymlcurrently builds the WASM bundle but doesn't browser-execute it.Thanks for the great library.