2-of-3 multisig Zcash transparent wallet (t3… addresses), built on
@bitgo/utxo-lib.
Three people each hold one key. Any two of them can move the funds; any single key
alone cannot. A t3 address is a transparent P2SH wrapping the redeem script
OP_2 <pk1> <pk2> <pk3> OP_3 OP_CHECKMULTISIG. This CLI derives the address, watches it
through a free block explorer (Blockbook — no API key), and sweeps the funds out with a
2-of-3 signature.
Transparent multisig only. No shielded (
z) support. Test with small amounts first.
npm install
Commands run with tsx (no build needed). To compile to dist/: npm run build.
Every participant runs this on their own machine (offline is ideal):
npm run keygen
It prints a pubkey and a WIF.
pubkey with the coordinator.WIF secret. It is the private key. Never send it to anyone.Collect the 3 pubkeys, then copy config.example.json → config.json and fill in:
| field | meaning |
|---|---|
pubkeys |
the 3 compressed pubkeys, in a fixed order — the order defines the address. |
withdrawTo |
destination transparent address (t1…/t3…) for withdrawals. |
signerWIFs |
any 2 of the 3 WIF private keys. Only needed to withdraw. |
explorerUrl |
optional Blockbook URL (default https://blockbook.zec.zelcore.io; or EXPLORER_URL env). |
feeZat |
optional fee override in zatoshis (default: ZIP-317 conventional fee). |
Persist the pubkey order (or the printed redeemScript). Funds are unspendable without it.
config.json, secrets.json, .env are git-ignored — never commit real keys.
npm run address -- config.json # prints your t3… address
Send ZEC to that t3 address from any wallet — an ordinary transparent send. This tool
never needs the depositing wallet’s key.
npm run balance -- config.json # confirmed / unconfirmed balance + UTXO count
npm run utxos -- config.json # list the confirmed UTXOs
Wait for confirmations before withdrawing.
withdrawTo)Two steps for safety. First build + sign + review — this does not send:
npm run withdraw -- config.json
Prints from/to, amounts, fee, txid, a decoded summary, and the raw hex. Review it. Then send:
npm run withdraw -- config.json --broadcast
Confirm with npm run balance or the explorer. To push an already-reviewed hex separately:
npm run broadcast -- <hex> config.json
| piece | file | what it does |
|---|---|---|
| Address | src/address.ts |
deriveMultisig2of3 → P2SH scriptPubKey → t3 base58check. |
| Explorer | src/explorer.ts |
Blockbook v2 REST (/utxo, /address, /tx, /sendtx). Free, no key. |
| Withdraw | src/withdraw.ts |
Sweeps all confirmed UTXOs into one output, minus fee. |
| Sign | src/spend.ts |
Builds a v5 tx, prevOutScriptType: 'p2sh-p2ms', signs with 2 of 3 keys. |
| Sighash | src/zip244.ts |
ZIP-244 transparent sighash (v5 tx). Verified against reference test vectors. |
| Fee | src/fee.ts |
ZIP-317 conventional fee (deterministic). |
Run the sighash test vectors: npm run build && node test/zip244.vectors.js.
@bitgo/utxo-lib. Keep the dependency current across future upgrades.feeZat.t2 (not t3) and need a
testnet explorer URL. Decode any hex with zcash-cli decoderawtransaction <hex> if you have a node.MIT