Threshold signatures on Solana? Dude, it's like splitting your private into pieces so no one person can screw you over. You got n people holding shares, and you need t of them to sign anything. Miss that threshold? Nothing happens. Super secure, and the signature looks just like a normal one on chain. No multisig bloat.
I usually set up a 2-of-3 for my test wallets. Why? If one device gets hacked or lost, you're not toast. Sound familiar? Yeah, that's the magic.
Okay, picture your private as a puzzle. Instead of one guy hoarding all pieces, you hand them out to, say, 5 friends. But you only need 3 to put it together and sign a tx. Fewer than that? Useless. The cool part? No one ever sees the full. It's all math wizardry called MPC-multi party computation.
And on Solana, it's fast as hell. No waiting for slow chains. Transactions cost like 0.000005 SOL in fees, same as regular ones. But way safer than leaving your seed phrase on your phone.
The thing is, regular multisig on Solana? It works, but it's clunky. Everyone's sigs show up on chain, screaming "hey, we're a team!" TSS? Invisible. Just one clean sig. Hackers hate that.
Multisig is easy with Squads or whatever, but TSS is next level. Here's a quick compare:
| TSS | Multisig | |
|---|---|---|
| On chain look | Single sig. Stealthy. | Multiple sigs. Obvious. |
| exposure | Never reconstruct full. | Each signer holds full. |
| Change players? | Reshare without new address. | New vault usually. |
| Fees | ~0.000005 SOL | Higher, more data. |
See? TSS wins for privacy and flexibility. In my experience, teams using this sleep better.
We're using solana mpc tss lib. It's TypeScript, ZenGo compatible, works on devnet/mainnet. Production ready, they say.
That's it. Two minutes tops.
Before multi party chaos, try solo MPC signing. Builds confidence.
import { createMPCSigner, MPCKeypair } from 'solana mpc tss lib';
import { Connection, clusterApiUrl, PublicKey, LAMPORTSPERSOL } from '@solana/web3.js'; const connection = new Connection(clusterApiUrl('devnet')); async function quickSign() { const signer = await createMPCSigner(); const keypair = new MPCKeypair(signer); console.log('Pubkey:', keypair.publicKey.toBase58()); // Airdrop some SOL await connection.requestAirdrop(keypair.publicKey, LAMPORTSPERSOL); // Send 0.001 SOL somewhere const tx = await connection.requestAirdrop(keypair.publicKey, 1000000); // Wait, no-use their createTransferTx if you want. const signedTx = await keypair.signTransaction(tx); const sig = await connection.sendTransaction(signedTx, [keypair]); console.log('Sig:', sig);
} quickSign();
Test it. Get SOL from faucet. Send to a burner. Boom, signed without a real private. Feels weird, right?
Alright, ramp up. Generate three participants. Aggregate keys with threshold 2. Then sign as a group.
Here's code. Copy paste friendly.
import { TSSCli } from 'solana mpc tss lib';
import { clusterApiUrl } from '@solana/web3.js'; const cli = new TSSCli('devnet'); // Generate three keypairs
const p1 = await cli.generate();
const p2 = await cli.generate();
const p3 = await cli.generate(); console.log('P1:', p1.publicKey.toBase58());
console.log('P2:', p2.publicKey.toBase58());
console.log('P3:', p3.publicKey.toBase58()); // Aggregate for 2-of-3
const aggKey = cli.aggregateKeys([p1.publicKey, p2.publicKey, p3.publicKey], 2);
console.log('Aggregate pubkey:', aggKey.toBase58()); // Get recent blockhash
const blockhash = await cli.recentBlockHash(); // Party 1 Step 1
const step1P1 = await cli.aggregateSignStepOne('Send 0.001 SOL to buddy', blockhash); // Party 2 Step 1 (simulate)
const step1P2 = await cli.aggregateSignStepOne('Send 0.001 SOL to buddy', blockhash); // In real, different parties. // Now Step 2 for P1
const step2P1 = await cli.aggregateSignStepTwo(JSON.stringify(step1P1), p1.secretKey, [step1P1.publicNonce, step1P2.publicNonce]); // You'd collect step2 from P2 too, then:
const finalSig = await cli.aggregateSignaturesAndBroadcast([step2P1, / step2P2 /], aggKey);
Wait, full workflow needs coordination. Like Discord or a server. I use a simple Express app to shuttle nonces between parties. Hacky but works.
Don't just sign messages. Actual SOL transfer. From the lib:
import { createTransferTx } from 'solana mpc tss lib'; const tx = await createTransferTx( connection, aggKey, // your aggregate pubkey new PublicKey('YourFriendsAddressHere'), 1000000 // 0.001 SOL lamports
); // Then do the multi step signing on this tx.serialize() or whatever the message is.
Pro tip: Always grab fresh blockhash. Solana txs expire quick- like 150 slots.
Potential issue? One party offline. That's why 2-of-3. But if two go dark? Stuck. Plan backups.
The lib mimics solana tss CLI. Run commands like:
Check balances: solana tss get balance PUBKEY. Airdrop too.
Don't wanna boilerplate? Use TSSWallet.
const wallet = new TSSWallet('devnet');
await wallet.generateKeypair(); const balance = await wallet.getBalance(somePubkey);
console.log(balance / LAMPORTSPERSOL, 'SOL'); // Aggregate existing
wallet.aggregateKeys([pk1, pk2, pk3], 2);
One object. Handles MPC ops. I usually wrap this in my apps.
Blockhash stale? Regenerate. Error: "Invalid nonce." Parties mismatched step1 outputs.
Network? Stick to devnet first. Mainnet beta same code, swap cluster.
Threshold too high? Start 1-of-1, then 2-of-2, build up.
refresh? TSS lets you rotate shares without new pubkey. Advanced, but killer for long term. Delete old shares after.
Common gotcha: Shares private. Never share secretKey bytes. Pubkeys and nonces? Safe to broadcast.
Async signing over network? Use WebSockets. Parties ping nonces real time.
Fees today? Still dirt cheap, under 0.000005 SOL per sig. But Solana congestion spikes-budget 0.001.
For teams: 3-of-5 solid. Lose two? Fine. Enterprise? 5-of-9 or whatever.
Integrate with wallets like ZenGo? Lib's compatible. Their Rust CLI too, if you're low level.
Why does this matter? Hacks hit single keys daily. TSS? Attacker needs t shares at once. Tough.
Build a dApp? Expose TSS signer via API. Users connect devices, sign via MPC.
Or DAO treasury. 3-of-7 validators sign spends. No seed phrases emailed around.
In my experience, start small. Testnet transfers. Then real money on devnet. Mainnet last.
One more: BLS curves? Solana digs BN254. Efficient. BLS12-381 coming, higher sec.
Change players? Reshare. Same pubkey, new shares. No migration pain.
Steps vague in lib docs, but basically: Gather old t shares, generate new n shares for same secret. Delete olds.
Game changer for teams. Member leaves? Refresh without tx.
Honestly, that's the secret sauce. Multisig can't touch it.