Skip to content

fix: P1 security & performance hardening (v2.1.0) - #6

Merged
Mctursh merged 9 commits into
mainfrom
fix/p1-security-and-perf-hardening
May 20, 2026
Merged

Mctursh merged 9 commits into
mainfrom
fix/p1-security-and-perf-hardening

Conversation

@Mctursh

@Mctursh Mctursh commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

P1 fixes surfaced by the multi-agent code review, shipped as v2.1.0
(@hermis/wallet-standard-base → 1.1.0). Packages are already published
to npm at latest; this PR is the source-of-record for the changes.

Note: three changes are breaking on local-key signing / adapter setup
paths. Migration steps are in docs/migration/v1-to-v2.mdx. Shipped as
minor per maintainer decision (v2.0 is fresh).

Security / correctness

  • Kit signature bytescreateTransactionSendingSignerFromWallet now
    bs58.decodes the signature instead of returning UTF-8 of the base58
    string.
  • Off-chain message domain separator — local-key signMessage
    (Keypair / CryptoKeyPair / KeyPairSigner) now prepends
    0xff "solana offchain" so a signature can't be replayed as a
    transaction signature. External wallets unaffected.
  • Explicit network required — removed RPC-URL substring inference;
    getStandardWalletAdapters(adapters, endpoint, network) and
    createWalletConnectionManager(adapters, network, …) now require an
    explicit network. StandardWalletAdapter.setNetwork() added.
  • sendTransaction overload — dropped the alt 3-arg form that silently
    created a devnet connection on a transaction-shaped first arg.
  • .length memo bug — adapter-set memoization missed equal-length swaps.
  • Debug logs strippedStandardWalletAdapter no longer logs unsigned
    tx bytes / wallet objects.

Performance

  • Memoized WalletContext.Provider value (kills full-tree re-renders).
  • Bounded wallet-standard:app-ready dispatch (10×1s → 4-shot backoff).
  • useSolanaTransaction: stop on terminal status + exponential backoff.

Release / tooling

  • changeset access → public; ignore (private) enterprise-core; mark
    sample-dapp private.
  • Renamed prerelease script → enter-prerelease (it was an npm
    lifecycle hook silently forcing the next dist-tag).

Docs

  • All docs.hermis.iodocs.hermis.dev; Live Demo → https://hermis.dev/.
  • Migration guide sections for off-chain prefix + explicit network.

Published to npm (latest)

  • @hermis/solana-headless-core@2.1.0
  • @hermis/solana-headless-adapter-base@2.1.0
  • @hermis/solana-headless-react@2.1.0
  • @hermis/wallet-standard-base@1.1.0

Test plan

  • pnpm build — all 8 packages green
  • New unit tests pass (factories, offchain-message)
  • sample-react-dapp connect/sign/send verified in browser
  • Published to npm at latest

🤖 Generated with Claude Code

Mctursh added 8 commits May 19, 2026 11:16
… off-chain message prefix

Three P1 fixes to `@hermis/solana-headless-core`.

1. createTransactionSendingSignerFromWallet now bs58-decodes signature
   bytes instead of returning UTF-8 of the base58 string. Previously
   the returned 88-byte buffer of ASCII was never going to validate
   as a 64-byte Ed25519 signature for any downstream Kit consumer.

2. sendTransaction and signAndSendTransaction lose their 3-arg
   alternative form (transaction, wallet, options). The form silently
   created a hard-coded devnet Connection when the first argument
   looked like a transaction (`'instructions' in arg` etc.) — a Kit
   Rpc with an instructions field would route the call to devnet
   without telling the caller. The form was undocumented and unused
   by internal callers.

3. signMessage on local-key paths (Keypair, CryptoKeyPair,
   KeyPairSigner) now prepends the Solana off-chain message domain
   separator (0xff + "solana offchain" + version + format + length)
   before signing. Without it, a caller signing user-supplied bytes
   in a headless backend can be tricked into producing a valid
   transaction signature. External wallet adapters apply this header
   themselves; those paths are untouched.

BREAKING CHANGE: signatures produced by in-SDK local-key signers no
longer verify against the raw message bytes; verifiers must apply
the same domain header. See docs/migration/v1-to-v2.mdx.

BREAKING CHANGE: sendTransaction/signAndSendTransaction require the
4-arg form (connection, transaction, wallet, options).
Removes RPC-URL substring cluster inference (typosquat-friendly) and
plumbs an explicit `network` argument end-to-end from HermisProvider
through useStandardWalletAdapters into StandardWalletAdapter and the
mobile adapter.

Also bundles the related perf and correctness work in the same
packages so a single review pass covers them:

- StandardWalletAdapter / utils.ts no longer ship debug console.log
  calls that leaked unsigned tx bytes and wallet objects to the
  page console (extension-readable). Error reporting kept.
- useStandardWalletAdapters and HermisProvider memoise the wallet
  set by name-joined key instead of `.length`, so swaps that keep
  the array length stable are no longer missed.
- WalletContext.Provider value wrapped in useMemo so useWallet()
  consumers stop re-rendering on every parent render.
- `wallet-standard:app-ready` re-dispatch loop reduced from 10×1s
  to a 4-shot backoff (100ms / 300ms / 1s / 3s).
- useSolanaTransaction now stops polling on terminal status and
  uses exponential backoff (1s → 30s).

Removed exports:
- getInferredNetworkFromEndpoint (both adapter-base and wallet-standard)
- detectClusterFromEndpoint (wallet-standard)

New exports:
- StandardWalletAdapter.setNetwork(network)
- chain.ts: networkToChainId, SOLANA_*_CHAIN constants

BREAKING CHANGE: getStandardWalletAdapters(adapters, endpoint, network)
and createWalletConnectionManager(adapters, network, ...) now require
an explicit `network` argument. StandardWalletAdapter.setRpcEndpoint
no longer derives the cluster — call setNetwork(network) for that.
See docs/migration/v1-to-v2.mdx.
Updates all getStandardWalletAdapters / createWalletConnectionManager
examples for the new explicit-network signature, and adds two
migration sections to v1-to-v2.mdx:

- Off-chain message domain separator (with verifier-side sample code)
- Explicit network requirement (with before/after diffs)
`pnpm version-packages` output for the P1 security & perf changeset.

- @hermis/solana-headless-core: 2.0.0 → 2.1.0
- @hermis/solana-headless-adapter-base: 2.0.0 → 2.1.0
- @hermis/solana-headless-react: 2.0.0 → 2.1.0
- @hermis/wallet-standard-base: 1.0.0 → 1.1.0

react-core's bump was manually corrected from the 3.0.0 changesets
produced (stale pre-release accumulator state); intended minor bump
is 2.1.0 to stay aligned with the core/adapter-base release cycle.

Exits the "next" pre-release mode in .changeset/pre.json.
- changeset config access: restricted → public (these scopes are public)
- ignore @hermis/solana-headless-enterprise-core (not ready; has known
  auth bugs and was never published)
- mark sample-dapp private so the demo never publishes to npm
- rename `prerelease` script → `enter-prerelease`; `prerelease` is an
  npm lifecycle pre-hook for `release`, so it was silently re-entering
  changeset pre-release mode on every `pnpm release` and pushing the
  `next` dist-tag instead of `latest`
Domain moved from hermis.io to hermis.dev. Update all docs.hermis.io
references across the root README, CONTRIBUTING, package READMEs
(adapter-base, react-core — these ship to npm), and mint.json
metadata. Also point the Live Demo anchor at https://hermis.dev/.
Match the root-path base used for the hermis.dev custom domain (also
landed on main) and keep the __DEV__ define so @hermis/errors emits
full error messages in dev.
@Mctursh Mctursh changed the title Fix/p1 security and perf hardening fix: P1 security & performance hardening (v2.1.0) May 20, 2026
@Mctursh
Mctursh merged commit 0726897 into main May 20, 2026
2 of 3 checks passed
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