npm.io
0.2.1 • Published 21h ago

@nikicat/browser-web3-signer-darwin-x64

Licence
MIT
Version
0.2.1
Deps
0
Size
4.9 MB
Vulns
0
Weekly
0

browser-web3-signer

Sign EVM and TRON transactions and messages from the command line using your own browser wallet (MetaMask, Rabby, TronLink, …). A command opens a local page in your browser, you approve in your wallet, and the result is printed. The private key never leaves the browser — this tool only routes the request and reads the result back.

It's a Rust reimplementation of the browser-signing capability of mcp-wallet-signer, with a CLI as the interface for agents (no MCP). The core is a library so it can be embedded from other languages; TypeScript adaptors (viem/ethers) over a managed Rust bridge subprocess are planned (see Status).

How it works

  CLI command ──► local HTTP bridge (127.0.0.1) ──► opens browser tab
                        ▲                                   │
                        │  POST /api/complete/:id           ▼
                  result (addr / tx hash / sig) ◄──── you approve in your wallet

Each command starts a tiny localhost-only HTTP server, opens the browser to an approval page, blocks until you act in your wallet (or a 5-minute timeout), prints the result, and exits. Nothing binds a public interface; the bridge is 127.0.0.1 only.

Install

Prebuilt binaries: each GitHub release ships static binaries for linux x64/arm64 (musl, runs on any distro), macOS x64/arm64, and windows x64, plus a SHA256SUMS file. Download browser-web3-signer-<target>, verify, chmod +x.

npm (for the TypeScript binding, which spawns the binary for you):

npm install browser-web3-signer   # pulls the right platform binary as an optionalDependency

From source — requires a Rust toolchain (pinned to 1.95 via rust-toolchain.toml):

cargo build --release
# binary at target/release/browser-web3-signer

Usage

browser-web3-signer <evm|tron> <command> [flags]

Global flags (any command): --browser <name> (open a specific browser instead of the default), --print (print the approval URL but don't open a browser), --json (machine- readable JSON on stdout; human text otherwise). Progress/prompts go to stderr, results to stdout.

EVM
browser-web3-signer evm connect --chain 1
browser-web3-signer evm send-transaction --to 0x… --value 1000000000000000 --chain 1
browser-web3-signer evm sign-message --message "hello"
browser-web3-signer evm sign-typed-data --file ./typed-data.json    # {domain,types,primaryType,message}

Built-in chains: Ethereum (1), Sepolia (11155111), Polygon (137), Arbitrum (42161), Optimism (10), Base (8453), Avalanche (43114), BNB Smart Chain (56). --value and the fee flags are in wei.

TRON
browser-web3-signer tron connect
browser-web3-signer tron send-transaction --to T… --amount 1000000          # SUN (1 TRX = 1e6 SUN)
browser-web3-signer tron trigger-contract --contract T… --selector 'transfer(address,uint256)' \
    --params '[{"type":"address","value":"T…"},{"type":"uint256","value":"1"}]'
browser-web3-signer tron sign-message --message "hello"
browser-web3-signer tron deploy-contract --abi-file ./abi.json --bytecode 0x…

Networks: mainnet, shasta, nile. Signing and transaction building happen browser-side in TronLink's tronWeb; the Rust side only routes requests.

Serve (control API for language bindings)
browser-web3-signer serve --chain evm     # prints the bound port, then blocks

Runs the bridge on a stable port for the process lifetime and exposes POST /api/v1/request (body is a request {type, …}; opens the wallet, blocks, returns {success, result} or {success:false, error, code?}) and GET /api/v1/health. A language binding spawns this and drives the wallet over HTTP — see the TypeScript binding. Honors the global --browser / --print flags for how the approval page opens.

Configuration (env)

Variable Default Meaning
BROWSER_WEB3_EVM_PORT 3847 Preferred bridge port for EVM
BROWSER_WEB3_EVM_CHAIN 1 Default EVM chain id
BROWSER_WEB3_TRON_PORT 3848 Preferred bridge port for TRON
BROWSER_WEB3_TRON_NETWORK mainnet Default TRON network
BROWSER Browser binary to open (else OS default)
BROWSER_WEB3_SIGNER_BIN Explicit signer-binary path for the language bindings

The port is preferred, not mandatory: if it's already in use (a concurrent command, or a daemon), the command falls back to an OS-assigned ephemeral port instead of failing.

Development

just            # list recipes
just ci         # fmt + taplo + clippy + build + test (what CI runs)
just test
just lint       # clippy -D warnings
just coverage   # cargo-llvm-cov summary

cargo/taplo/clippy are pinned and gated by CI and a prek pre-commit hook (.pre-commit-config.yaml). See ARCHITECTURE.md for the design and the rationale behind the key decisions.

Manual real-wallet test (local anvil)

scripts/manual-test-evm.sh drives your real browser wallet against a throwaway local anvil chain: it starts anvil, has you connect (adding the local network via --rpc-url), funds your address with a cheat code, then walks through sign-message, sign-typed-data, send-transaction, and an ERC-20 transfer — verifying each result on-chain with cast. You only approve each step in the wallet; no testnet, no faucet, no real funds. Requires foundry (anvil/cast/forge) + jq and a cargo build.

scripts/manual-test-tron.sh is the TronLink counterpart, driven by a throwaway local tronbox/tre node in Docker. TRON has no anvil/cast, so a small TronWeb helper (scripts/tron/tron-tool.ts, run directly by node ≥ 22.6) is the funding + verification layer: it funds your address from the node's genesis key, deploys/mints a demo TRC-20, and verifies signatures and receipts. TronLink can't be pointed at a node from the CLI, so the one-time setup is to add http://127.0.0.1:9090 as a custom node in TronLink and select it; the script prints the exact steps. Same five stages, same "you only approve in the wallet" flow. Requires Docker, node ≥ 22.6, forge, jq, cargo build.

One TRON-specific wrinkle: TronLink only knows a chainId for its built-in networks and never queries a custom node's, so TIP-712 typed-data signing fails on a local node ("Current chainId cannot be null") until you inject it once via scripts/tron/inject-chainid.js (pasted into TronLink's service-worker console — the script's setup step explains it). The other four stages don't need it. DEBUG_RPC=1 logs all wallet→node traffic through a proxy for diagnosis.

Status

Working today: the one-shot CLI for EVM and TRON (connect, send/trigger/deploy, message + typed-data signing), with an embedded approval UI per chain.

E2E browser tests: a Playwright suite drives a mock wallet against the real Rust bridge for both EVM and TRON (connect, sign, send/trigger/deploy, reject, cancel, address mismatch), testing the full browser interaction flow. Run with just e2e-setup && just e2e (one-time npm install + Chromium download, then just e2e). CI runs it as a dedicated job on every push and PR.

Control API (serve): browser-web3-signer serve --chain evm|tron runs the bridge on a stable port for its lifetime and exposes POST /api/v1/request + GET /api/v1/health, printing the bound port to stdout. This is the long-lived mode language bindings spawn and drive.

TypeScript binding (ts/): a WalletSignerClient that spawns and supervises the serve subprocess and drives it over /api/v1, plus a viem transport + hybrid account — so a TS program signs with the user's browser wallet, and the persistent tab skips the reconnect prompt across calls. Published to npm as browser-web3-signer; the Rust binary ships as per-platform optionalDependencies, so npm install is all it takes — no Rust toolchain needed.

Go binding (go/): EVMClient / TronClient — a thin client that spawns and supervises the serve subprocess and drives it over /api/v1, covering both EVM and TRON. Every operation takes a context.Context; coded errors surface as typed values (WrongWalletAddressError); results are go-ethereum types (common.Address, common.Hash, hexutil.Bytes) that plug directly into the go-ethereum code most consumers already have.

Persistent sessions in Rust: hold a single EvmSigner / TronSigner and reuse it — same stable port, same effect (the pattern the reference's long-lived WalletSigner uses; no daemon required).

Deferred: a full multi-client daemon (discovery file, auth, request queue, SSE), warranted only if several independent processes must share one connected tab. See ARCHITECTURE.md.

License

MIT — see LICENSE.