approveAsMulti method

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

Approves a pending multisig transaction (non-final approval).

This method is used for all approvals except the final one. It adds the approver's signature to the pending transaction. When the approval count reaches threshold-1, you must use asMulti instead for the final approval that will execute the transaction.

The method automatically:

  • Checks if the approver is a valid signatory
  • Verifies the approver hasn't already approved
  • Ensures the transaction isn't already complete
  • Prevents using approveAsMulti for the final approval (use asMulti instead)

Parameters:

  • approverAddress: The address of the signatory approving the transaction (must be a signatory)
  • signingCallback: Callback function to sign the approval transaction
  • callData: The SCALE-encoded call data to approve
  • 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:

  • 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 with enough approvals
  • StateError if this would be the final approval (must use asMulti instead)
  • Exception if the transaction submission fails

Example:

// Alice initiated the transaction, now Bob approves
final response = await multisig.approveAsMulti(
  approverAddress: bob.address,
  signingCallback: bob.sign,
  callData: callData, // From the initial response
);

Implementation

Future<MultisigResponse> approveAsMulti({
  required final String approverAddress,
  required final SigningCallback signingCallback,
  required final Uint8List callData,
  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);
  // Fetch current storage state
  final storage = await MultisigStorage.fetch(
    provider: provider,
    multisigPubkey: multisigAccount.multisigPubkey,
    callHash: callHash,
  );

  if (storage != null) {
    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)) {
      throw StateError(
        'Can\'t call `approveAsMulti`. Final approval required for this transaction, use `asMulti` to approve this transaction.',
      );
    }
  }

  // First/Middle approval - use approveAsMulti
  // approvals-till-now = (storage?.approvals.length ?? 0)
  // Adding approval ${approvals-till-now + 1} / ${signatories.threshold}
  final RuntimeCall approvalCall = _createApproveAsMultiRuntimeCall(
    signerAddress: approverAddress,
    maybeTimepoint: storage?.when,
    callHash: callHash,
    maxWeight: maxWeight,
  );

  final txHash = await _submitCall(
    call: approvalCall,
    signerAddress: approverAddress,
    signCallback: signingCallback,
    nonce: nonce,
    tip: tip,
    eraPeriod: eraPeriod,
  );

  return MultisigResponse(multisigAccount: multisigAccount, callData: callData, txHash: txHash);
}