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