Skip to content

feat(wallets): reject unsupported xBull extension versions before connecting - #838

Open
woahwhattheheck wants to merge 2 commits into
Stellar-split:mainfrom
woahwhattheheck:feat/771-xbull-version-gate
Open

feat(wallets): reject unsupported xBull extension versions before connecting#838
woahwhattheheck wants to merge 2 commits into
Stellar-split:mainfrom
woahwhattheheck:feat/771-xbull-version-gate

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 6, 2026

Copy link
Copy Markdown

Closes #771.

XBullAdapter called connect() on the extension without checking its
version, so an older build failed later with errors that did not name the real
cause.

Changes

src/wallets/adapters/XBullAdapter.ts:

  • MIN_XBULL_VERSION — exported, "1.4.0": the first version exposing
    the object-shaped connect() / sign() surface this adapter uses. Bump the
    constant if that is not where you would draw the line.
  • ExtensionVersionError — carries extension, foundVersion and
    requiredVersion, and names all three in the message.
  • The check runs before window.xbull.connect() is called, so an
    unsupported extension is rejected up front rather than part-way through.
  • compareExtensionVersions — exported, so callers can gate their own UI
    on the same comparison.
  • skipVersionCheck constructor option as an escape hatch.

new XBullAdapter() still works: the constructor takes an optional options
object, so no existing call site changes.

Where the version is read

The issue names window.xBullSDK.version, while this file declares
window.xbull. Builds differ in which handle carries the version, so the
adapter reads window.xbull.version and falls back to
window.xBullSDK.version. Both are covered by tests.

Both connection paths are gated

getAddress() also calls connect() when there is no cached key, so the
check runs there too. It does not re-run once a key is cached — there is a
test pinning that, since re-checking on every address read would turn an
extension update mid-session into a spurious failure.

Two details

  • Comparison is numeric, not lexical. "1.10.0" is newer than "1.9.0",
    which a string comparison gets backwards. Missing trailing components count
    as zero, so "1.4" and "1.4.0" compare equal.
  • It fails closed, including when no version is reported at all. A build
    too old to expose a version is almost certainly too old to provide the API
    surface used here, and connecting anyway produces exactly the cryptic
    failure this check exists to replace. The error says "version not reported"
    rather than inventing one, and skipVersionCheck covers a build that is
    known-good but silent. Happy to make absence permissive instead if you would
    rather only an explicit low version fail.

Validation

  • npx vitest run test/xbullAdapter.version.test.ts — 27/27 passing. Covers
    newer/equal/older versions, the extension's connect() not being called
    when the gate rejects, all three fields named on the error, the
    xBullSDK fallback, absent and unparseable versions, skipVersionCheck,
    "not installed" still taking precedence, both gated entry points, no
    re-check once connected, and the comparator itself.
  • Offline: window is stubbed, so no browser or extension is needed.
  • npx tsc --noEmit reports 210 errors on this branch and 210 on unmodified
    main
    — identical, with none in
    src/wallets/adapters/XBullAdapter.ts. That baseline is pre-existing and
    unrelated to this change.

Note on overlap

The version comparison here is deliberately local to this module. #775
(PR #837) introduces an equivalent helper for the Ledger adapter; if both
land, the two are worth consolidating into a shared util in a follow-up. I
kept them separate so neither PR depends on the other.

Note on pre-release versions

parseVersion requires every dot-separated component to be digits, so a
pre-release like "1.4.0-beta" returns null and the extension is treated as
unsupported. That is deliberate — the gate exists to refuse a build whose
compatibility cannot be established — but it does mean a legitimate RC build is
refused, and skipVersionCheck is the intended escape hatch. Worth a decision
if xBull ships pre-release builds to real users.

(The duplication with #837's comparison logic is noted at the end of this
description; same offer applies — a shared helper once the second lands.)

…necting

The adapter connected without checking the extension version, so an older
build failed later with errors that did not name the cause. The version is now
read before connect() is called and compared against MIN_XBULL_VERSION,
rejecting with ExtensionVersionError naming the extension, the version found
and the version required.

The version is read from window.xbull.version, falling back to
window.xBullSDK.version, since builds differ in which handle carries it.
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.

Check XBull extension version before attempting connection

1 participant