Summary
The Module Federation split (host frontend/, frontend-wizard-remote/, frontend-dashboard-remote/) left lib/soroban.ts as three independent copies instead of one shared module:
frontend/src/lib/soroban.ts — 778 lines
frontend-wizard-remote/src/lib/soroban.ts — 574 lines
frontend-dashboard-remote/src/lib/soroban.ts — 363 lines
They're not just different subsets — diff shows 328 changed lines between the host and wizard-remote copies, and 239 between wizard-remote and dashboard-remote. Root cause: none of frontend-wizard-remote/frontend-dashboard-remote are listed in the root package.json's workspaces array (["backend", "frontend", "sdk/js", "packages/cli", "evm", "zk"]), so they can't consume a shared internal package via the workspace: protocol — each was bootstrapped with its own copy-pasted subset of Soroban client code instead.
Why this matters (not hypothetical)
This already caused a real, shipped bug: PR #215 added preflightSimulate() to frontend/src/lib/soroban.ts, and it was manually re-added to frontend-wizard-remote/src/lib/soroban.ts — but the wizard-remote copy of the preflight transaction builder only carried 4 of create_commitment's 9 arguments (missing resolver, oracle, schema_id, attestors, vote_threshold), because that logic had to be independently re-derived by hand instead of imported from one place. It shipped, and only failed in e2e-sandbox's real-RPC run — the mocked frontend tests couldn't catch it because the mock doesn't validate argument count against the real contract ABI. See the fix in PR #215 for the concrete failure mode (HostError: Error(WasmVm, UnexpectedSize) / MismatchingParameterLen).
Every future fix or feature added to one copy (auth handling, error decoding, RPC pooling, new contract calls) now has to be remembered and manually re-applied to the other two, or it silently drifts exactly like this did.
Suggested fix
Extract the shared Soroban client (RPC pooling, fetchArbitrator, submitCreateCommitment, preflightSimulate, error decoding, wallet adapters) into a proper internal package — e.g. packages/soroban-client — add it to the root workspace, and have frontend, frontend-wizard-remote, and frontend-dashboard-remote all depend on it via workspace:*. Module Federation's shared-dependency mechanism (already used for react/react-dom/@tanstack/react-query per docs/module-federation.md) can share it at runtime too, so this doesn't have to mean three separate bundled copies even after the source is unified.
Summary
The Module Federation split (host
frontend/,frontend-wizard-remote/,frontend-dashboard-remote/) leftlib/soroban.tsas three independent copies instead of one shared module:frontend/src/lib/soroban.ts— 778 linesfrontend-wizard-remote/src/lib/soroban.ts— 574 linesfrontend-dashboard-remote/src/lib/soroban.ts— 363 linesThey're not just different subsets —
diffshows 328 changed lines between the host and wizard-remote copies, and 239 between wizard-remote and dashboard-remote. Root cause: none offrontend-wizard-remote/frontend-dashboard-remoteare listed in the rootpackage.json'sworkspacesarray (["backend", "frontend", "sdk/js", "packages/cli", "evm", "zk"]), so they can't consume a shared internal package via theworkspace:protocol — each was bootstrapped with its own copy-pasted subset of Soroban client code instead.Why this matters (not hypothetical)
This already caused a real, shipped bug: PR #215 added
preflightSimulate()tofrontend/src/lib/soroban.ts, and it was manually re-added tofrontend-wizard-remote/src/lib/soroban.ts— but the wizard-remote copy of the preflight transaction builder only carried 4 ofcreate_commitment's 9 arguments (missingresolver,oracle,schema_id,attestors,vote_threshold), because that logic had to be independently re-derived by hand instead of imported from one place. It shipped, and only failed ine2e-sandbox's real-RPC run — the mocked frontend tests couldn't catch it because the mock doesn't validate argument count against the real contract ABI. See the fix in PR #215 for the concrete failure mode (HostError: Error(WasmVm, UnexpectedSize)/MismatchingParameterLen).Every future fix or feature added to one copy (auth handling, error decoding, RPC pooling, new contract calls) now has to be remembered and manually re-applied to the other two, or it silently drifts exactly like this did.
Suggested fix
Extract the shared Soroban client (RPC pooling,
fetchArbitrator,submitCreateCommitment,preflightSimulate, error decoding, wallet adapters) into a proper internal package — e.g.packages/soroban-client— add it to the root workspace, and havefrontend,frontend-wizard-remote, andfrontend-dashboard-remoteall depend on it viaworkspace:*. Module Federation's shared-dependency mechanism (already used forreact/react-dom/@tanstack/react-queryperdocs/module-federation.md) can share it at runtime too, so this doesn't have to mean three separate bundled copies even after the source is unified.