asMulti method

Future<MultisigResponse> asMulti({
  1. required Uint8List callData,
  2. required String approverAddress,
  3. required SigningCallback signingCallback,
  4. int eraPeriod = 64,
  5. BigInt? tip,
  6. int? nonce,
  7. Weight? maxWeight,
})

Submits the final approval and executes the multisig transaction.

This method must be used for the final approval (when approval count equals threshold-1). Unlike approveAsMulti, this method not only adds the approval but also executes the pending call on-chain. The transaction will be executed immediately upon successful approval.

The method automatically:

  • Verifies that prior approvals exist (storage must not be null)
  • Checks if the approver is a valid signatory
  • Ensures the approver hasn't already approved
  • Confirms this is the final approval needed
  • Executes the call upon successful approval

Parameters:

  • callData: The SCALE-encoded call data to execute
  • approverAddress: The address of the signatory providing the final approval (must be a signatory)
  • signingCallback: Callback function to sign the execution transaction
  • eraPeriod: The era period for transaction mortality (default: 64 blocks)
  • tip: Optional tip to prioritize transaction inclusion
  • nonce: Optional nonce override for the transaction
  • maxWeight: Maximum weight for the call execution (defaults to refTime: 1s, proofSize: 10KB)

Returns: A MultisigResponse containing the multisig account details, call data, and transaction hash.

Throws:

  • StateError if no prior approvals exist (storage is null)
  • ArgumentError if the approver is not a signatory of the multisig account
  • StateError if the approver has already approved this transaction
  • StateError if the transaction is already complete
  • StateError if this is not the final approval (use approveAsMulti instead)
  • Exception if the transaction submission or execution fails

Example:

// Charlie provides the final approval and executes the transaction
// (assuming threshold is 3 and Alice & Bob have already approved)
final response = await multisig.asMulti(
  callData: callData,
  approverAddress: charlie.address,
  signingCallback: charlie.sign,
);
print('Transaction executed with hash: ${response.txHash}');

Implementation

Future<MultisigResponse> asMulti({
  required final Uint8List callData,
  required final String approverAddress,
  required final SigningCallback signingCallback,
  final int eraPeriod = 64,
  final BigInt? tip,
  final int? nonce,
  Weight? maxWeight,
}) async {
  maxWeight ??= Weight(
    refTime: BigInt.from(1000000000), // 1 second of execution time
    proofSize: BigInt.from(10000), // 10KB proof size
  );

  if (!multisigAccount.containsAddress(approverAddress)) {
    throw ArgumentError('$approverAddress is not a signatory of this multisig');
  }

  final callHash = Hasher.blake2b256.hash(callData);

  final storage = await MultisigStorage.fetch(
    provider: provider,
    multisigPubkey: multisigAccount.multisigPubkey,
    callHash: callHash,
  );

  if (storage == null) {
    throw StateError(
      'Multisig Storage is null, indicating that the first approval has not been submitted yet',
    );
  } else {
    if (storage.hasApproved(approverAddress)) {
      throw StateError('Address $approverAddress has already approved this transaction');
    }
    if (storage.isComplete(multisigAccount.threshold)) {
      throw StateError(
        'Transaction is already complete with ${storage.approvals.length} approvals',
      );
    }
    if (storage.isFinalApproval(multisigAccount.threshold) == false) {
      throw StateError(
        'Not enough approvals for final execution. Current approvals: ${storage.approvals.length}, Required for asMulti: ${multisigAccount.threshold - 1}. Use approveAsMulti instead.',
      );
    }
  }
  // This will be the final approval - use asMulti to execute
  // Creating final approval to execute transaction
  final RuntimeCall approvalCall = _createAsMultiRuntimeCall(
    signerAddress: approverAddress,
    maybeTimepoint: storage.when,
    callData: callData,
    maxWeight: maxWeight,
  );

  final txHash = await _submitCall(
    call: approvalCall,
    signerAddress: approverAddress,
    signCallback: signingCallback,
    nonce: nonce,
    eraPeriod: eraPeriod,
    tip: tip,
  );
  return MultisigResponse(multisigAccount: multisigAccount, callData: callData, txHash: txHash);
}