Repository navigation
feat(wallets): add xBull, verify the wallet network, and test the hooks package - #175
Open
aigbagbobila wants to merge 9 commits into
Open
aigbagbobila wants to merge 9 commits into
aigbagbobila wants to merge 9 commits into
Conversation
…smatch xBull is one of the most used Stellar wallets, but the provider only knew Freighter, Albedo, Rabet and Hana, so testnet users could not connect. Add an `xbull` provider that drives the API the extension injects as `window.xBullSDK` (the extension half of `@creit.tech/xbull-wallet-connect`, so no new dependency), with connect, public-key and signing paths plus missing-extension errors worded like the Rabet/Hana ones. Signing also silently produced a signature for the wallet's own network: a wallet switched to Mainnet while the app targets Testnet failed later with an opaque passphrase mismatch. Read the network where the adapter supports it (Freighter `getNetworkDetails`/`getNetwork`, xBull `getNetwork`), compare it with the passphrase the app is about to sign with, and refuse with a message naming both networks and how to fix it. The comparison is re-read whenever the app switches networks with `setNetwork`, and the connected wallet's passphrase is exposed as `walletNetworkPassphrase`. Tests cover xBull connect success/failure/signing, reading the network from both adapters, a matching network still signing, a mismatch blocking the signature, and `setNetwork` re-reading the wallet. Closes SorobanForge#121 Closes SorobanForge#120 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The dashboard only offered "Connect Freighter", so xBull (and Albedo, Rabet and Hana) could not be reached from the UI even though the provider supports them. Replace the button with a picker that renders `WALLET_PROVIDER_OPTIONS` from `@stellar-starter-kit/wallets`, so the picker lists xBull and any wallet added later cannot be missing from it. Connect failures now surface the provider's own message (e.g. "xBull wallet extension is not installed.") instead of a generic one, and the menu closes on selection or on a click outside it. Closes SorobanForge#121
`packages/hooks` had no test script and no tests, so the mount guards, polling and error capture in `useStream` were only exercised by clicking around the dashboard. Add vitest with jsdom and @testing-library/react, a minimal config and a `test` script so Turborepo's `pnpm test` picks the suite up. The tests cover a successful load, a null stream, an error from `getStream`, a failing `claimable` falling back to zero, the no-client/no-id short circuit and a polling tick driving a second `getStream` call with fake timers. Closes SorobanForge#119
`parseTokenAmount` runs on the create-stream path, but nobody had measured it.
Add a vitest bench (`pnpm --filter @stellar-starter-kit/utils bench`) with the
old implementation kept inline as the baseline, then replace the validation
regex and `unsigned.split('.')` allocations with a character scan and cache
`10n ** BigInt(decimals)` instead of re-deriving it on every call.
Measured on Node 24 (median of 7 runs, representative inputs): ~1.9-2.0M
ops/sec before, ~4.0-4.5M ops/sec after, i.e. ~2x and 520ns -> 220ns per call.
Attribution: the regex alone is worth only a few percent; caching the power of
ten is what removes most of the cost.
Behaviour is unchanged: the accept-language of the scanner is the same as the
regex plus its three explicit rejections, and a 400k-call fuzz over valid,
invalid and random inputs produces identical values and identical throw
messages. The full utils suite passes unchanged (replayed assertion by
assertion, since this workspace has no node_modules).
Closes SorobanForge#111
The README layout, the architecture package table and the in-app docs still described the wallet package as Freighter/Albedo/Rabet/Hana. Closes SorobanForge#121
|
@aigbagbobila Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@aigbagbobila is attempting to deploy a commit to the techalhaji's projects Team on Vercel. A member of the Team first needs to authorize it. |
Collaborator
|
fix the failing ci please. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds an xBull wallet adapter, verifies that the connected wallet is on the network the app is signing for, gives
packages/hooksa real test suite, and replaces the regex in the amount parser with a measured, ~2x faster implementation.Closes #121
Closes #120
Closes #119
Closes #111
All five commits are conventional-commit formatted and scoped by package:
feat(wallets): add an xBull adapter and block signing on a network mismatchfeat(web): list every wallet in a dashboard pickertest(hooks): add vitest and cover useStreamperf(utils): benchmark the amount parser and drop the regexdocs: list xBull among the supported wallets#121 —
feat(wallets): add an xBull adapterWhat changed
WalletTypeis now'freighter' | 'albedo' | 'rabe' | 'hana' | 'xbull'andWALLET_PROVIDERSgainedXBULL: 'xbull'.connect('xbull')→ public key, andsignTransaction→ signed XDR, both implemented against the API the xBull extension injects aswindow.xBullSDK(getAddress(),signTransaction({ xdr, opts: { networkPassphrase, address } }),getNetwork()). That global is the extension half of@creit.tech/xbull-wallet-connect, which is how the official SDK talks to the extension whenpreferredTargetis'extension'— so the browser extension is driven directly without adding a runtime dependency, the same way the existing Rabet/Hana adapters consumewindow.rabet/window.hanaWallet.'xBull wallet extension is not installed.'(connect) and'xBull extension is missing.'(signing), matching the Rabet/Hana messages, and an SDK error payload ({ error: { code, message } }) is surfaced with the wallet's own message.WALLET_PROVIDER_OPTIONS(new export) is the single source of truth for the picker:[{ id, label }]for all five providers, typed asWalletProviderType, andWALLET_PROVIDERSis nowsatisfies Record<string, WalletType>so adding a wallet in one place but not the other is a compile error rather than a runtimeUnsupported wallet provider./streams) replaced its hard-coded Connect Freighter button with a wallet picker that rendersWALLET_PROVIDER_OPTIONS, so xBull is selectable (and Albedo/Rabet/Hana are no longer unreachable from the UI). The menu closes on selection or on a click outside, hasaria-haspopup/aria-expanded/role="menu"/role="menuitem", and surfaces the provider's own error message instead of a generic "Connection failed."README.mdlayout tree,ARCHITECTURE.mdclient-layer table and the in-app docs page now list xBull.Tests (
packages/wallets/src/WalletProvider.test.tsx,index.test.ts)window.xBullSDKis absent → the exact error message above.{ xdr, opts: { networkPassphrase, address } }.['freighter', 'xbull', 'albedo', 'rabe', 'hana']) and labels xBull correctly.#120 —
feat(wallets): verify the wallet network before signingWhat changed
readWalletNetworkPassphrase(provider)asks each adapter for the network it is on, where the adapter supports it:getNetworkDetails().networkPassphrase, falling back togetNetwork()on older builds that lack it.window.xBullSDK.getNetwork().networkPassphrasewhen the extension build exposes it.nulland the check is skipped (documented in code).signTransactionnow compares the passphrase it is about to sign with (opts.networkPassphraseorpassphraseForNetwork(network)) against the wallet's own. On a mismatch it refuses before the wallet is called, with a message naming both networks and both fixes:The comparison is a state-backed, cancelled-safe effect keyed on
(activeProvider, network), so connecting, auto-reconnecting from localStorage, disconnecting andsetNetwork(...)all keep it consistent (AC "setNetworkupdates the comparison").walletNetworkPassphraseis exposed on the context for consumers that want to display the wallet's network.New exported helpers, unit-testable on their own:
TESTNET_PASSPHRASE,MAINNET_PASSPHRASE,passphraseForNetwork,networkLabelForPassphrase,networkIdForPassphrase,networkMismatchMessage.Tests
freighter.signTransactionis not called, and the message contains both passphrases plus thesetNetwork('MAINNET')instruction.getNetworkDetailsrejects →getNetworkis used.setNetwork('MAINNET')re-reads the wallet network, so an in-app switch unblocks signing.#119 —
test(hooks): add vitest and coveruseStreampackages/hooks/package.json: added atestscript (vitest run) and the dev dependenciesvitest@^4.1.10,jsdom@^29.1.1,@testing-library/react@^16.3.2,react-dom. Turborepo picks the suite up through the existingtesttask, sopnpm testnow runs it.packages/hooks/vitest.config.ts: minimal config (environment: 'jsdom',globals: trueso Testing Library's automatic cleanup is registered).packages/hooks/src/index.test.tsxcovers a successful load (stream + claimable), anullstream, an error fromgetStream, a failingclaimablefalling back to0n, the no-client/no-id short circuit, and — with fake timers — a polling tick that drives a secondgetStreamcall (and a third).pnpm-lock.yaml: thepackages/hooksimporter now records those dependencies using resolutions already present in the lockfile, so CI'spnpm install --frozen-lockfilestays consistent (verified locally: "Lockfile is up to date, resolution step is skipped").#111 —
perf(utils): benchmark the parse path and remove the regexMeasured first, per the issue.
packages/utils/src/index.bench.ts(new,pnpm --filter @stellar-starter-kit/utils bench) benchmarks the current implementation and keeps the previous implementation inline as a labelled baseline, so the before/after numbers stay reproducible instead of living only in this description.Representative inputs (
'1','10','100','1000','12.5','123.456789'), Node 24, median of runs:parseXlmToStroops(typing digits)parseXlmToStroops(mixed amounts)Attribution (same harness, single-variable variants): replacing the regex with a character scan alone is worth only a few percent; caching
10n ** BigInt(decimals)is what removes most of the cost, and the two changes together give ~2x. Both landed in one change because they touch the same expression:isPlainDecimal(raw)scans character codes instead of running^-?\d*(\.\d*)?$, with the same accept-language as the regex plus its three explicit rejections ('','.','-').unsigned.split('.')is replaced withindexOf/slice(no intermediate array), and10n ** BigInt(decimals)with a module-levelPOWERS_OF_TENtable (assertDecimalsalready boundsdecimalsto0..38).Evidence that behaviour is identical (this workspace has no
node_modules, so the suite was replayed rather than executed):packages/utils/src/index.test.ts(54 checks, all passing) re-run against the edited source, including the invalid-input and round-trip cases.1e-7/1e21/NaN/Infinity, and 200k random strings over0123456789.-+eE,× decimals0/6/7/18) comparing the new implementation with the previous one: identical values and identical error messages on every call.handleCreate(it never parsed per keystroke), so no debounce was added or removed.How this was verified locally
pnpm installwas intentionally not run in this workspace (nonode_modules) per the task constraints, so verification used the tools that are available offline plus the exact dependencies where possible:prettier@3.9.5(the pinned version) with the repository's options (--single-quote --print-width 100 --trailing-comma all --end-of-line lf)--checked every added/modified.ts,.tsxand.mdfile: all clean. The pre-existing repo files also pass with these flags, confirming the flags reproduce CI'sformat:check.pnpm install --frozen-lockfilereaches "Lockfile is up to date, resolution step is skipped" with the hand-updated lockfile (it then stops only because there is no package store and the sandbox is offline); the lockfile diff is limited to the newpackages/hooksimporter block.package.jsonfiles.packages/utilssemantics — suite replayed assertion-by-assertion plus the differential fuzz described above.pnpm lint,pnpm typecheck,pnpm test,pnpm buildand thecargocontract jobs requirenode_modules/toolchain that cannot be installed here. No Rust files are touched, so the contracts job is unaffected. The new TypeScript was reviewed against the installed types' declarations (e.g.@stellar/freighter-api@6.0.1'sgetNetwork/getNetworkDetailsreturn shapes, fetched from the registry) and follows the patterns already used by the existing wallet tests and hooks.Manual check list for a reviewer with dependencies installed
Type of Change
Checklist
Notes / follow-ups
@creit.tech/xbull-wallet-connect'spreferredTarget: 'website'popup bridge) is intentionally out of scope: the issue asks for an adapter with connect/public-key/sign paths and extension-consistent errors. Adding the SDK later would only need the same three calls behind the existingXBullInterface./escrowpage still has its own single-wallet connect button. It is outside the scope of feat(wallets): add an xBull adapter #121 ("the wallet picker on the dashboard"), and the newWALLET_PROVIDER_OPTIONSlist makes it a small follow-up to give it the same picker.readWalletNetworkPassphraseneeds a new branch.