Skip to content

feat: add getPublicKey across all chains - #194

Open
teyrebaz33 wants to merge 1 commit into
open-wallet-standard:mainfrom
teyrebaz33:feat/get-public-key
Open

feat: add getPublicKey across all chains#194
teyrebaz33 wants to merge 1 commit into
open-wallet-standard:mainfrom
teyrebaz33:feat/get-public-key

Conversation

@teyrebaz33

Copy link
Copy Markdown

Summary

Adds getPublicKey(walletName, chainId) to the Node SDK, Python SDK, and CLI. Eliminates the mnemonic round-trip workaround for public key access.

Closes #157

Problem

TON wallet contract initialization (WalletContractV5R1) requires the raw Ed25519 public key. The only workaround was calling exportWallet() to get the mnemonic, re-deriving the keypair externally, then zeroing secret material — partially undermining key isolation even with careful zeroing.

Solution

// Node SDK
const pubkey = getPublicKey('my-wallet', 'ton', undefined, vaultDir);
// → 'a1b2c3...' (32-byte hex, Ed25519)

const evmPubkey = getPublicKey('my-wallet', 'evm', undefined, vaultDir);
// → '02a1b2...' (33-byte compressed SEC1)
# CLI
ows wallet public-key --wallet my-wallet --chain ton
ows wallet public-key --wallet my-wallet --chain evm --json
# Python SDK
pubkey = get_public_key('my-wallet', 'ton', None, vault_dir)

Return format

Curve Chains Format
Ed25519 TON, Solana, Sui 32-byte hex (64 chars)
secp256k1 EVM, Bitcoin, Cosmos, Tron, XRPL, Filecoin, Spark 33-byte compressed SEC1 (66 chars, 02/03 prefix)

Changes

  • ows-signer: derive_public_key() added to ChainSigner trait, implemented for all 10 chains
  • ows-lib: get_public_key(wallet, chain, index, vault_path) with 8 unit tests
  • bindings/node: getPublicKey() exported via NAPI
  • bindings/python: get_public_key() exported via PyO3
  • ows-cli: ows wallet public-key --wallet --chain [--index] [--json]

Tests

test get_public_key_evm_returns_33_bytes ... ok
test get_public_key_ton_returns_32_bytes ... ok
test get_public_key_solana_returns_32_bytes ... ok
test get_public_key_bitcoin_returns_33_bytes ... ok
test get_public_key_is_deterministic ... ok
test get_public_key_different_index_yields_different_key ... ok
test get_public_key_fails_for_nonexistent_wallet ... ok
test get_public_key_all_chains ... ok

8 passed — 144 existing ows-lib tests also pass

Security

Public keys are not secret — exposing them does not weaken the security model. Private key material never leaves the OWS signing core.

Adds derive_public_key() to ChainSigner trait and wires it through
ows-lib, Node binding, Python binding, and CLI.

Closes open-wallet-standard#157

Problem
-------
TON wallet contract initialization (WalletContractV5R1) requires the
raw Ed25519 public key. The only workaround was exportWallet() to get
the mnemonic, re-derive externally, then zero the secret material —
partially undermining key isolation.

Solution
--------
Add get_public_key(wallet, chain, index) that returns the raw public
key hex without exposing any secret material.

Return format:
- Ed25519 chains (TON, Solana, Sui): 32-byte hex (64 chars)
- secp256k1 chains (EVM, Bitcoin, Cosmos, Tron, XRPL, Filecoin, Spark):
  33-byte compressed SEC1 hex (66 chars, starts with 02 or 03)

Changes
-------
- ows-signer: derive_public_key() added to ChainSigner trait
- ows-signer: implemented for all 10 chains
- ows-lib: get_public_key() with 8 unit tests (144 existing pass)
- bindings/node: getPublicKey() exported via NAPI
- bindings/python: get_public_key() exported via PyO3
- ows-cli: ows wallet public-key --wallet --chain [--index] [--json]
@teyrebaz33
teyrebaz33 requested a review from njdawn as a code owner April 5, 2026 00:33
@vercel

vercel Bot commented Apr 5, 2026

Copy link
Copy Markdown

@teyrebaz33 is attempting to deploy a commit to the MoonPay Team on Vercel.

A member of the Team first needs to authorize it.

@0xultravioleta

Copy link
Copy Markdown
Collaborator

Thanks for taking this on. A couple of things to sort out before it can go further.

First, the branch is a fair way behind main, so it needs a rebase, and the rebase is where the build breaks. main now has NEAR and Nano signers that this branch predates. Since this PR adds derive_public_key as a trait method with no default body, once you rebase onto main the workspace will not compile (E0046, missing trait item) until NanoSigner and NearSigner also implement derive_public_key. So please add the method to those two as part of the rebase, alongside the chains already covered here.

Second, the implementation decrypts the wallet with an empty passphrase and then calls expose() on the secret. That only works for wallets created without a passphrase, and it pulls the raw private key into memory just to derive a public key. The public key can be derived from the key material without exposing the secret, so it would be better to derive it directly rather than decrypt-and-expose.

So: rebase and add derive_public_key to the NEAR and Nano signers, and derive the pubkey without decrypting the secret. Final sign-off here is @njdawn's. Thanks again.

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.

Feature request: getPublicKey(walletName, chainId) - needed for TON wallet contract initialization

2 participants