approveAsMulti method
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 transactioncallData: The SCALE-encoded call data to approveeraPeriod: The era period for transaction mortality (default: 64 blocks)tip: Optional tip to prioritize transaction inclusionnonce: Optional nonce override for the transactionmaxWeight: 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);
}