Skip to content

refactor(wallet): delegate Base wallet resolution to @blockrun/core - #26

Merged
VickyXAI merged 1 commit into
mainfrom
feat/adopt-core-wallet
Aug 11, 2026
Merged

refactor(wallet): delegate Base wallet resolution to @blockrun/core#26
VickyXAI merged 1 commit into
mainfrom
feat/adopt-core-wallet

Conversation

@VickyXAI

Copy link
Copy Markdown
Contributor

Summary

  • scanWallets, listDiscoveredWallets, importWallet, loadWallet, getOrCreateWallet, and getWalletAddress now delegate to the shared @blockrun/core kernel instead of re-implementing the canonical resolution order (env → ~/.blockrun/.session → legacy wallet.key) in this package
  • src/wallet.ts drops ~128 lines of duplicated logic; public signatures unchanged
  • BLOCKRUN_HOME now overrides the wallet directory (from core's path resolution); unset, behaviour is identical
  • Solana wallet resolution stays SDK-local — core has no Solana key store yet

Why

The duplication was not theoretical: this SDK fixed provider-wallet takeover in 3.7.1 (#14) while core kept resolving provider wallet.json files first until @blockrun/core@0.1.0 — for three weeks the blockrun CLI signed x402 payments with whatever key a planted ~/.<app>/wallet.json supplied. One implementation cannot drift from itself.

Requires @blockrun/core@^0.1.0 (0.1.1 now on npm; the lockfile resolves it). Earlier versions carry the provider-takeover defect and must not be used.

Validation

  • All ten canonical-selection regression tests pass against the delegated implementation, including the ones proving a discovered wallet is never adopted automatically and that a file cannot claim an address it holds no key for
  • Rebased onto 3.12.0 (Router Core V3 bundling): 259 tests, typecheck, lint, ESM/CJS/DTS build all green

🤖 Generated with Claude Code

scanWallets, listDiscoveredWallets, importWallet, loadWallet, getOrCreateWallet
and getWalletAddress now call the shared kernel instead of re-implementing the
canonical order (env -> ~/.blockrun/.session -> legacy wallet.key) here.

The duplication was not theoretical. This SDK fixed provider-wallet takeover in
3.7.1 (#14); core kept resolving provider wallet.json files first until
@blockrun/core@0.1.0, so for three weeks the blockrun CLI signed x402 payments
with whatever key a planted ~/.<app>/wallet.json supplied. One implementation
cannot drift from itself.

Public signatures are unchanged. All ten canonical-selection regression tests
pass against the delegated implementation, including the ones proving a
discovered wallet is never adopted automatically and that a file cannot claim
an address it holds no key for.

- importWallet(address) maps onto core's adoptWallet(); core's importWallet()
  takes a raw private key and is a different operation, which is why the two
  could not be reconciled before core 0.1.0.
- BLOCKRUN_HOME now overrides the wallet directory (path resolution comes
  from core). Unset, behaviour is identical.
- Solana wallet resolution stays SDK-local — core has no Solana key store yet.
- Rebased onto 3.12.0 (Router Core V3 bundling); lockfile resolves
  @blockrun/core@0.1.1 from npm.
VickyXAI pushed a commit that referenced this pull request Aug 11, 2026
…review)

Seven-pass review (testing/maintainability/security specialists +
Claude/Codex adversarial + Codex structured) found two criticals, both
reproduced empirically:

- @blockrun/core publishes ESM-only, so keeping it external made the CJS
  entry require("@blockrun/core") and throw ERR_PACKAGE_PATH_NOT_EXPORTED
  for every CommonJS consumer at load — invisible to the all-ESM CI.
  Core moves to devDependencies and tsup noExternal bundles it into both
  formats (the router-core pattern), which also freezes the reviewed
  kernel bytes into dist instead of floating on ^0.1.0.
- WALLET_DIR/WALLET_FILE were snapshotted at module load while core
  re-resolves BLOCKRUN_HOME per call: change the env after import (dotenv
  ordering) and getOrCreateWallet() minted a fresh key that saveWallet()
  wrote over the user's real, possibly funded ~/.blockrun/.session with
  no backup. Paths now resolve per call; the exported WALLET_FILE_PATH /
  WALLET_DIR_PATH stay import-time snapshots for API compatibility and
  are documented as such.

Also from the review:
- Pin the security properties that moved into core with six new tests:
  env-beats-file, .session-before-legacy, unprefixed-key normalization,
  the post-import BLOCKRUN_HOME split-brain regression, re-adopt without
  backup, and a fixture-home guard so a core path regression can never
  silently point the suite at the developer's real home.
- afterEach now restores BLOCKRUN_WALLET_KEY/BASE_CHAIN_WALLET_KEY too.
- scanWallets() TSDoc matches core's semantics (derived address, address
  field ignored); importWallet() documents the name collision with
  core's importWallet(rawKey); wallet.ts header states what stays local
  and marks BLOCKRUN_HOME security-sensitive; solana-wallet.ts documents
  why it is deliberately SDK-local; AGENTS.md/README/CHANGELOG synced.

Verified: 265 tests green; packed tarball into a clean project — CJS
require and ESM import both load, node_modules contains only @blockrun/llm.
VickyXAI pushed a commit that referenced this pull request Aug 11, 2026
)

One kernel, one wallet: resolution, discovery, and adoption now come from
@blockrun/core (bundled at build time), reviewed and hardened before merge.
@VickyXAI
VickyXAI merged commit 596ba28 into main Aug 11, 2026
3 checks passed
@VickyXAI

Copy link
Copy Markdown
Contributor Author

Merged via 7291e7a after a seven-pass review (testing / maintainability / security specialists + Claude and Codex adversarial passes + Codex structured review). The delegation itself verified clean — core 0.1.1's resolution order, provider-file exclusion, derived-address adoption, backup-on-replace, and 0o600 writes all check out, and core's scanWallets is stricter than the code it replaces. Two integration criticals were found, reproduced, and fixed before merge:

  1. CJS entry was dead on arrival: @blockrun/core publishes ESM-only, so the externalized require("@blockrun/core") in dist/index.cjs threw ERR_PACKAGE_PATH_NOT_EXPORTED for every CommonJS consumer at load — invisible to the all-ESM CI. Core is now bundled at build time (devDependency + tsup noExternal, the router-core pattern), which also freezes the reviewed kernel bytes instead of floating on ^0.1.0.
  2. Path snapshot split-brain → fund loss: WALLET_DIR/WALLET_FILE were captured at module load while core re-resolves BLOCKRUN_HOME per call. Setting the env after import (dotenv ordering) made getOrCreateWallet() mint a fresh key and saveWallet() write it over the real, possibly funded ~/.blockrun/.session with no backup — reproduced. Paths now resolve per call, pinned by a regression test.

Also landed: six tests pinning the security properties that moved into core (env precedence, .session-before-legacy, normalization, re-adopt-no-backup, fixture-home guard), scanWallets()/importWallet() TSDoc corrections, BLOCKRUN_HOME documented as security-sensitive, and AGENTS.md/README/CHANGELOG sync. Follow-ups noted for core upstream: dual CJS/ESM publish, and whitespace-only env keys currently fall through to the file wallet instead of failing loudly.

@VickyXAI
VickyXAI deleted the feat/adopt-core-wallet branch August 11, 2026 16:41
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.

1 participant