MultisigAccount constructor

MultisigAccount({
  1. required List<String> addresses,
  2. required int threshold,
})

Creates a new MultisigAccount instance with automatic address derivation.

This factory constructor performs the following operations:

  1. Validates the threshold (must be ≥2 and ≤ number of signatories)
  2. Deduplicates the provided addresses
  3. Validates the signatory count (2-100 signatories)
  4. Converts addresses to public keys
  5. Sorts public keys deterministically
  6. Derives the multisig account address using Substrate's algorithm

Parameters:

  • addresses: List of signatory addresses (SS58 encoded). Duplicates are automatically removed.
  • threshold: Number of approvals required for execution (must be between 2 and number of signatories)

Returns: A MultisigAccount instance with the derived multisig address.

Throws:

Example:

final multisigAccount = MultisigAccount(
  addresses: [alice.address, bob.address, charlie.address],
  threshold: 2,
);
// Any 2 of the 3 signatories can approve transactions

Implementation

factory MultisigAccount({required final List<String> addresses, required final int threshold}) {
  // Validate threshold
  if (threshold < 2) {
    throw ArgumentError('Threshold must be at least 2, got $threshold');
  }

  // Remove duplicates
  final uniqueAddresses = addresses.toSet().toList(growable: false);

  // Validate count
  if (uniqueAddresses.length < 2) {
    throw ArgumentError('At least 2 signatories required, got ${uniqueAddresses.length}');
  }

  if (uniqueAddresses.length > 100) {
    throw ArgumentError('Maximum 100 signatories allowed, got ${uniqueAddresses.length}');
  }

  if (threshold > uniqueAddresses.length) {
    throw ArgumentError(
      'Threshold ($threshold) cannot exceed signatories (${uniqueAddresses.length})',
    );
  }

  // Convert to public keys and sort
  final pubkeysWithAddresses = <Uint8List>[];

  for (final address in uniqueAddresses) {
    try {
      final pubkey = Address.decode(address).pubkey;
      pubkeysWithAddresses.add(pubkey);
    } catch (e) {
      throw ArgumentError('Invalid address: $address');
    }
  }

  // Sort by public key bytes
  pubkeysWithAddresses.sort((final a, final b) => a.compareBytes(b));

  // Generate multisig address
  final multisigPubkey = _deriveMultisigAddress(pubkeysWithAddresses, threshold);

  return MultisigAccount._(
    threshold: threshold,
    publicKeys: pubkeysWithAddresses,
    multisigPubkey: multisigPubkey,
  );
}