Skip to content

fix(bridge): block cross-chain destination backed by random key#88

Open
graysonhyc wants to merge 1 commit into
mainfrom
fix/bridge-block-unrecoverable-curve-destination
Open

fix(bridge): block cross-chain destination backed by random key#88
graysonhyc wants to merge 1 commit into
mainfrom
fix/bridge-block-unrecoverable-curve-destination

Conversation

@graysonhyc
Copy link
Copy Markdown
Collaborator

Summary

Bridge silently sent funds to an unrecoverable address when the source wallet was imported via single-curve private key (zerion wallet import --evm-key or --sol-key).

OWS's importWalletPrivateKey uses the provided key for its curve's chains and generates a random key for the other curve. exportWallet returns only the originally-imported secret — the random key is locked to the CLI vault. Auto-derived cross-chain destinations sent funds to the random-key address.

Fix

resolveDestination now checks WALLET_ORIGIN before falling back to the source wallet or accepting --to-wallet. Curve mismatch throws with guidance to pass --to-address <addr> to a wallet the user actually controls.

Source origin Target Old behavior New behavior
EVM_KEY Solana Send to random ed25519 addr Throws, requires --to-address
SOL_KEY EVM Send to random secp256k1 addr Throws, requires --to-address
MNEMONIC either Send to derived addr Unchanged (real keys, recoverable via backup)
any --to-address <addr> Honored Honored

Repro (before fix)

zerion wallet import --evm-key   # paste EVM privkey
zerion bridge ethereum USDC 10 solana USDC   # auto-derives random SOL receiver, funds lost

After fix

$ zerion bridge ethereum USDC 10 solana USDC
Wallet "myWallet" was imported from an EVM private key, so its Solana address is
backed by a random key that is not recoverable from your imported secret.
Bridging to it would lock funds to this CLI vault.

Pass --to-address <solana-pubkey> to send to a Solana wallet you control,
or --to-wallet <name> for a wallet imported via mnemonic or Solana private key.

Known limitation

Wallets created via ows CLI directly (bypassing zerion wallet import) have no origin marker and default to MNEMONIC (permissive). OWS-direct wallets actually derive from a real mnemonic so both curves are real, so this default is correct. Pre-feature zerion-imported wallets without an origin marker fall through too — re-import to fix.

Reporter

Horjet via user feedback (--to-address flag they requested was already there; the actual bug was the auto-derive path).

Test plan

  • npm test — 303 pass, 0 fail
  • 4 new tests in resolve.test.mjs cover EVM_KEY→Solana, SOL_KEY→EVM, both via fallback and via --to-wallet, plus the --to-address bypass

🤖 Generated with Claude Code

When a wallet was imported via single-curve private key (`zerion wallet
import --evm-key` or `--sol-key`), OWS generates a random key for the
other curve so the wallet still exposes both EVM and Solana addresses.
`exportWallet` returns only the imported secret — the random key is not
recoverable outside the CLI vault.

Bridge auto-derived the cross-chain receiver from the source wallet's
other-curve address. For EVM-key wallets bridging to Solana (and the
reverse), this silently sent funds to an address the user could only
sign for from this CLI. Lose the vault, lose the funds.

Now `resolveDestination` checks wallet origin before falling back to
the source wallet or accepting `--to-wallet`. Curve mismatch throws
with guidance to pass `--to-address <addr>` to a wallet the user
actually controls. Mnemonic and OWS-direct wallets stay permissive
(their other-curve keys are real). 4 new tests cover both directions
and the explicit-address bypass.

Reported by Horjet in user feedback. The `--to-address` flag was
already supported; the gap was the auto-derive path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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