Multisig Account Setup
Description
Section titled “Description”This example demonstrates how to create multisig accounts using the KMD importMultisig() method.
Key concepts:
- A multisig account requires M-of-N signatures to authorize transactions
- The threshold (M) is the minimum number of signatures required
- The public keys (N) are the participants who can sign
- The multisig version parameter (currently always 1) defines the format
- The resulting multisig address is deterministically derived from the public keys, threshold, and version
Prerequisites
Section titled “Prerequisites”- LocalNet running (via
algokit localnet start) - Covered operations:
- generateKey() - Generate keys to use as multisig participants
- importMultisig() - Create a multisig account from public keys
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example kmd_client/08-multisig-setup.ts/** * Example: Multisig Account Setup * * This example demonstrates how to create multisig accounts using the KMD * `importMultisig()` method. * * Key concepts: * - A multisig account requires M-of-N signatures to authorize transactions * - The threshold (M) is the minimum number of signatures required * - The public keys (N) are the participants who can sign * - The multisig version parameter (currently always 1) defines the format * - The resulting multisig address is deterministically derived from the * public keys, threshold, and version * * Prerequisites: * - LocalNet running (via `algokit localnet start`) * * Covered operations: * - generateKey() - Generate keys to use as multisig participants * - importMultisig() - Create a multisig account from public keys */
import { decodeAddress } from '@algorandfoundation/algokit-utils';import { cleanupTestWallet, createKmdClient, createTestWallet, printError, printHeader, printInfo, printStep, printSuccess,} from '../shared/utils.js';
/** * Format a byte array for display, showing first and last few bytes */function formatBytesForDisplay(bytes: Uint8Array, showFirst = 4, showLast = 4): string { const hex = Buffer.from(bytes).toString('hex'); if (bytes.length <= showFirst + showLast) { return hex; } const firstBytes = hex.slice(0, showFirst * 2); const lastBytes = hex.slice(-(showLast * 2)); return `${firstBytes}...${lastBytes}`;}
async function main() { printHeader('KMD Multisig Account Setup Example');
const kmd = createKmdClient(); let walletHandleToken = ''; const walletPassword = 'test-password';
try { // ========================================================================= // Step 1: Create a Test Wallet // ========================================================================= printStep(1, 'Creating a test wallet');
const testWallet = await createTestWallet(kmd, walletPassword); walletHandleToken = testWallet.walletHandleToken;
printSuccess(`Test wallet created: ${testWallet.walletName}`); printInfo(`Wallet ID: ${testWallet.walletId}`);
// ========================================================================= // Step 2: Generate 3 Keys for Multisig Participants // ========================================================================= printStep(2, 'Generating 3 keys to use as multisig participants');
const participantAddresses: string[] = []; const numParticipants = 3;
for (let i = 1; i <= numParticipants; i++) { const result = await kmd.generateKey({ walletHandleToken }); participantAddresses.push(result.address.toString()); printInfo(`Participant ${i}: ${result.address}`); }
printSuccess(`Generated ${numParticipants} participant keys`); printInfo(''); printInfo('These addresses will be used to create a 2-of-3 multisig account.');
// ========================================================================= // Step 3: Convert Addresses to Public Keys // ========================================================================= printStep(3, 'Converting addresses to public keys');
const publicKeys: Uint8Array[] = participantAddresses.map(addr => { const decoded = decodeAddress(addr); return decoded.publicKey; });
printInfo('Public keys extracted from addresses:'); publicKeys.forEach((pk, i) => { printInfo(` Participant ${i + 1}: ${formatBytesForDisplay(pk)} (${pk.length} bytes)`); });
printInfo(''); printInfo('Note: Each Algorand address encodes a 32-byte public key.'); printInfo('The address also includes a 4-byte checksum for error detection.');
// ========================================================================= // Step 4: Create the Multisig Account with importMultisig() // ========================================================================= printStep(4, 'Creating a 2-of-3 multisig account with importMultisig()');
const threshold = 2; // Minimum signatures required const multisigVersion = 1; // Multisig format version
const multisigResult = await kmd.importMultisig({ walletHandleToken, publicKeys, threshold, multisigVersion, });
const multisigAddress = multisigResult.address.toString();
printSuccess('Multisig account created successfully!'); printInfo(''); printInfo('ImportMultisigResponse fields:'); printInfo(` address: ${multisigAddress}`); printInfo(''); printInfo('Parameters used:'); printInfo(` publicKeys: ${numParticipants} participant keys`); printInfo(` threshold: ${threshold} (minimum signatures required)`); printInfo(` multisigVersion: ${multisigVersion}`);
// ========================================================================= // Step 5: Explain the Threshold Parameter // ========================================================================= printStep(5, 'Understanding the threshold parameter');
printInfo(''); printInfo('What is the threshold?'); printInfo('-'.repeat(40)); printInfo(''); printInfo(`The threshold (${threshold}) is the minimum number of signatures required`); printInfo('to authorize any transaction from this multisig account.'); printInfo(''); printInfo(`With a ${threshold}-of-${numParticipants} configuration:`); printInfo(` - ${numParticipants} participants can potentially sign`); printInfo(` - At least ${threshold} signatures are required`); printInfo( ` - Any ${threshold} of the ${numParticipants} participants can authorize a transaction`, ); printInfo(''); printInfo('Common use cases:'); printInfo(' - 2-of-3: Standard security (recover if one key is lost)'); printInfo(' - 2-of-2: Joint control (both parties must agree)'); printInfo(' - 3-of-5: Committee/board decisions'); printInfo(' - 1-of-N: Any participant can act alone (hot wallet backup)');
// ========================================================================= // Step 6: Explain the Multisig Version Parameter // ========================================================================= printStep(6, 'Understanding the multisig version parameter');
printInfo(''); printInfo('What is the multisig version?'); printInfo('-'.repeat(40)); printInfo(''); printInfo( `The multisig version (${multisigVersion}) specifies the format of the multisig account.`, ); printInfo(''); printInfo('Currently, version 1 is the only supported version on Algorand.'); printInfo('This parameter exists for future compatibility if the multisig'); printInfo('format is ever updated.'); printInfo(''); printInfo('Always use version 1 unless Algorand documentation specifies otherwise.');
// ========================================================================= // Step 7: Show Relationship Between Keys and Address // ========================================================================= printStep(7, 'Relationship between public keys and multisig address');
printInfo(''); printInfo('How is the multisig address derived?'); printInfo('-'.repeat(40)); printInfo(''); printInfo('The multisig address is deterministically computed from:'); printInfo(' 1. The multisig version'); printInfo(' 2. The threshold value'); printInfo(' 3. The ordered list of public keys'); printInfo(''); printInfo('Important properties:'); printInfo(' - Same inputs always produce the same multisig address'); printInfo(' - Changing the order of public keys changes the address'); printInfo(' - Changing the threshold changes the address'); printInfo(' - The address encodes the complete multisig configuration'); printInfo(''); printInfo('Multisig address structure:'); printInfo(` ${multisigAddress}`); printInfo(''); printInfo('Participant addresses (order matters!):'); participantAddresses.forEach((addr, i) => { printInfo(` ${i + 1}. ${addr}`); });
// ========================================================================= // Step 8: Verify Multisig is Listed // ========================================================================= printStep(8, 'Verifying the multisig account is in the wallet');
const listResult = await kmd.listMultisig({ walletHandleToken });
printSuccess(`Wallet contains ${listResult.addresses.length} multisig address(es)`); printInfo(''); printInfo('Multisig addresses in wallet:'); listResult.addresses.forEach((addr, i) => { const marker = addr.toString() === multisigAddress ? ' (our new multisig)' : ''; printInfo(` ${i + 1}. ${addr}${marker}`); });
// ========================================================================= // Step 9: Summary of Multisig Operations // ========================================================================= printStep(9, 'What you can do with the multisig account');
printInfo(''); printInfo('Now that the multisig is imported, you can:'); printInfo(''); printInfo(' 1. RECEIVE FUNDS: Send Algo or ASAs to the multisig address'); printInfo(` Address: ${multisigAddress}`); printInfo(''); printInfo(' 2. SIGN TRANSACTIONS: Use signMultisigTransaction() to add'); printInfo(' signatures from participants whose keys are in this wallet'); printInfo(''); printInfo(' 3. EXPORT CONFIGURATION: Use exportMultisig() to get the'); printInfo(' full multisig parameters (keys, threshold, version)'); printInfo(''); printInfo(' 4. DELETE: Use deleteMultisig() to remove from the wallet'); printInfo(' (does not affect the blockchain account or funds)'); printInfo(''); printInfo('Note: To fully authorize a transaction, collect signatures from'); printInfo(`at least ${threshold} participants, then combine and submit.`);
// ========================================================================= // Cleanup // ========================================================================= printStep(10, 'Cleaning up test wallet');
await cleanupTestWallet(kmd, walletHandleToken); walletHandleToken = ''; // Mark as cleaned up
printSuccess('Test wallet handle released');
// ========================================================================= // Summary // ========================================================================= printHeader('Summary'); printInfo('This example demonstrated multisig account setup in KMD:'); printInfo(''); printInfo(' importMultisig()'); printInfo(' Parameters:'); printInfo(' - walletHandleToken: Session token for the wallet'); printInfo(' - publicKeys: Array of Uint8Array participant keys'); printInfo(' - threshold: Minimum signatures required (M in M-of-N)'); printInfo(' - multisigVersion: Format version (always 1)'); printInfo(' Returns:'); printInfo(' - address: The generated multisig Address'); printInfo(''); printInfo('Key takeaways:'); printInfo(' - Multisig requires M-of-N signatures to authorize transactions'); printInfo(' - The address is derived from version + threshold + ordered keys'); printInfo(' - Same configuration always produces the same address'); printInfo(' - Public keys are extracted from addresses using decodeAddress()'); printInfo(' - multisigVersion should always be 1 (current Algorand standard)'); printInfo(''); printInfo('Note: The test wallet remains in KMD (wallets cannot be deleted via API).'); } catch (error) { printError(`Error: ${error instanceof Error ? error.message : String(error)}`); printInfo(''); printInfo('Troubleshooting:'); printInfo(' - Ensure LocalNet is running: algokit localnet start'); printInfo(' - If LocalNet issues occur: algokit localnet reset'); printInfo(' - Check that KMD is accessible on port 4002');
// Cleanup on error if (walletHandleToken) { await cleanupTestWallet(kmd, walletHandleToken); }
process.exit(1); }}
main().catch(error => { console.error('Fatal error:', error); process.exit(1);});Other examples in KMD Client
Section titled “Other examples in KMD Client”- KMD Version Information
- Wallet Creation and Listing
- Wallet Session Management
- Key Generation
- Key Import and Export
- Key Listing and Deletion
- Master Key Export
- Multisig Account Setup
- Multisig Account Management
- Transaction Signing with KMD
- Multisig Transaction Signing with KMD
- Program Signing (Delegated Logic Signatures) with KMD
- Multisig Program Signing (Delegated Multisig Logic Signatures) with KMD