Skip to content

feat(abstract-utxo): add Pearl fixed-script wallet surface#9360

Draft
manojkumar138 wants to merge 1 commit into
masterfrom
CECHO-1791-add-pearl-to-abstract-utxo
Draft

feat(abstract-utxo): add Pearl fixed-script wallet surface#9360
manojkumar138 wants to merge 1 commit into
masterfrom
CECHO-1791-add-pearl-to-abstract-utxo

Conversation

@manojkumar138

Copy link
Copy Markdown
Contributor

Summary

Adds the Pearl (Duplex) fixed-script wallet surface to abstract-utxo, plus the sdk-coin-pearl registration shim. Follows #9347, which defined the coin in statics.

No utxo-lib changes — Pearl is served entirely through @bitgo/wasm-utxo. Per @OttoAllmendinger: no descriptors, no ordinals, no utxo-core, no utxo-bin.

PR 2 of 3. coinFactory / bitgo module wiring follows in PR 3 (CECHO-1792).

Changes

File Change
abstract-utxo/src/names.ts pearl / tpearl registered; Pearl added to getBaseNameFromMainnet
abstract-utxo/src/impl/pearl/ new — Pearl / Tpearl classes
abstract-utxo/src/impl/index.ts, src/index.ts export the new impl
sdk-coin-pearl/ new package — re-export + register() shim
tsconfig.packages.json register the new package
sdk-core/.../abstractUtxoCoinUtil.ts actionable error for coins with no utxo-lib network
6 × package.json + yarn.lock @bitgo/wasm-utxo ^4.21.1^4.27.0

The coin classes are ~15 lines each, mirroring impl/ltc/.

Taproot-only comes for free

The TDD sketched a getSupportedScriptTypes() override. It isn't needed — AbstractUtxoCoin.supportsAddressType() already delegates to wasm:

supportsAddressType(addressType: ScriptType2Of3): boolean {
  return fixedScriptWallet.supportsScriptType(this.name, addressType);
}

and wasm-utxo 4.27.0 reports only the taproot types for Pearl. Verified — abstract-utxo's p2tr spelling resolves correctly against wasm's p2trLegacy:

pearl p2sh -> false    p2shP2wsh -> false    p2wsh -> false
pearl p2tr -> true     p2trMusig2 -> true

supportsAddressChain() inherits this, so chains 30/31/40/41 are supported and 0/1/10/11/20/21 are not. Both are pinned in the new test rather than left implicit, so a regression in either layer is caught.

Why all six wasm-utxo bumps

Bumping abstract-utxo alone left two ranges in play, and yarn resolved two copies of the wasm binary — 4.27.0 nested under abstract-utxo, 4.21.1 hoisted at the root. Aligning all six collapses it to a single hoisted 4.27.0. The lockfile diff is one entry.

This is a dependency-hygiene change, not Pearl support being added to utxo-core / utxo-bin — no code in those packages changed.

sdk-core — deliberately not fully fixed

getUtxoCoinScriptTypes2Of3() and getUtxoCoinScriptTypesForWalletType() resolve statics' utxolibName into a utxo-lib network:

const network = utxolib.networks[coin.network.utxolibName as utxolib.NetworkName];

For Pearl that is undefined, and the code then threw a bare TypeError: invalid network. It now throws:

coin pearl has no utxo-lib network (utxolibName=pearl).
Script types for wasm-only coins must be resolved through @bitgo/wasm-utxo.

I stopped there rather than routing these through wasm, because sdk-core does not depend on @bitgo/wasm-utxo and adding a WASM binary to a core package is an architectural decision with bundle-size consequences for every consumer — not mine to make in this PR. There are no in-repo callers; both functions are public API consumed by wallet-platform / bitgo-ui. @OttoAllmendinger this is the spot that matters for WP onboarding — happy to take whichever direction you prefer (wasm dep in sdk-core, a lookup table, or handling it WP-side).

Test coverage

New abstract-utxo/test/unit/impl/pearl/unit/index.ts (7 tests), following the impl/btg convention: instantiation, chain, full name, testnet inheritance, taproot-only script types, taproot-only address chains, and an explicit assertion that no utxo-lib network exists for Pearl — no other checked-in coin has that profile, so it can't be inferred from an existing coin.

Pearl is deliberately not added to the shared utxoCoins fixtures. That infrastructure maps coin names to utxo-lib networks (getNetworkForCoinName throws for unknown coins, and getUtxoCoins() sorts by utxolib.getNetworkList() position), so Pearl has nothing to point at. Adding it would have meant reworking test infra shared by 17 suites, 8 of which resolve a utxolib network. The dedicated suite covers what this PR actually adds.

RecoveryProvider.forCoin is also left without a Pearl case — Blockchair has no Pearl support, and the existing fallthrough already throws ApiNotImplementedError, which is the correct outcome rather than a silent wrong provider.

Verification

Module Result
abstract-utxo 1552 passing (1545 → +7)
sdk-core 530 passing
statics 32269 passing
utxo-core 239 passing
utxo-bin 237 passing
utxo-descriptors 35 passing
utxo-ord 173 passing
utxo-staking 59 passing

Zero failures. abstract-utxo, sdk-coin-pearl, sdk-core, and all five wasm dependents build clean. Prettier clean; eslint clean (0 errors). Verified end-to-end that sdk-coin-pearl re-exports both classes and register is callable, and that getFullNameFromCoinName yields Pearl / Testnet Pearl.

Related

TICKET: CECHO-1791

🤖 Generated with Claude Code

Add the Pearl (Duplex) coin classes to abstract-utxo and the
sdk-coin-pearl registration shim, following PR 1 which defined the coin
in statics.

Pearl is served entirely through @bitgo/wasm-utxo and has no
@bitgo/utxo-lib network registration, so no utxo-lib changes are needed
here.

Taproot-only support needs no override. supportsAddressType already
delegates to fixedScriptWallet.supportsScriptType, which reports only
p2tr and p2trMusig2 for this coin, and supportsAddressChain inherits
that. The new test pins the behaviour so a regression in either layer is
caught.

Bump @bitgo/wasm-utxo to ^4.27.0, the first release carrying pearl and
tpearl in CoinName. All six utxo packages are bumped together rather
than abstract-utxo alone: mixed ranges made yarn resolve two copies of
the wasm binary, one hoisted and one nested.

sdk-core's getUtxoCoinScriptTypes2Of3 and
getUtxoCoinScriptTypesForWalletType resolve statics' utxolibName into a
utxo-lib network, which does not exist for Pearl. They now fail with an
actionable message pointing at wasm-utxo instead of a bare
`TypeError: invalid network`. Resolving those two through wasm is left
for the wallet-platform work, since sdk-core does not depend on
wasm-utxo today.

Pearl is deliberately left out of the shared utxoCoins test fixtures:
that infrastructure maps coin names to utxo-lib networks, which Pearl
does not have. Coverage lives in the dedicated impl/pearl suite instead.

Recovery via RecoveryProvider.forCoin is also left unimplemented for
Pearl - Blockchair has no Pearl support, and the existing default throws
ApiNotImplementedError.

TICKET: CECHO-1791

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@linear-code

linear-code Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

CECHO-1791

@manojkumar138

Copy link
Copy Markdown
Contributor Author

CI status: blocked only on npm package creation

Everything substantive is green. The single real failure is verify-npm-packages, with all-checks failing downstream of it:

Check Result
unit-test (20.x / 22.x / 24.x) ✅ pass
code-quality (lint / format / dependencies / commit-lint) ✅ pass
docker-build ✅ pass
verify-npm-packages ❌ fail
all-checks ❌ fail (aggregate — fails only because of the above)
These packages must be created on npm before publishing with trusted publishing.

1 package(s) not found on npm:

  - @bitgo/sdk-coin-pearl (modules/sdk-coin-pearl)

This is a prerequisite for any new package rather than something fixable in the diff: .github/actions/verify-npm-packages requires every non-private package to already exist on the registry, because npm trusted publishing needs the package created and its publisher configured up front.

Confirmed both scopes are missing, not just the one CI reports:

@bitgo/sdk-coin-pearl       -> NOT FOUND
@bitgo-beta/sdk-coin-pearl  -> NOT FOUND

# control
@bitgo/sdk-coin-btc         -> 2.13.27
@bitgo-beta/sdk-coin-btc    -> 1.0.1

Raised VL-7329 (assigned to @andyfischer-bitgo, Velocity / BitGoJS Trusted publishing) to create both packages and configure GHA as trusted publisher — same request as VL-6005 for sdk-coin-starknet. No change to this PR is needed once that lands; the check should pass on a re-run.

The alternative would be marking the package "private": true to force the gate green, but that makes it unpublishable and would leave PR 3's wiring pointing at something consumers can't install — so I'd rather wait for the npm setup than hide it.

@pranishnepal pranishnepal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 package(s) not found on npm:

  - @bitgo/sdk-coin-pearl (modules/sdk-coin-pearl)

CI's red and drafted - unsubscribing

@OttoAllmendinger OttoAllmendinger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm but check CI errors

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.

3 participants