asMulti method
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 executeapproverAddress: The address of the signatory providing the final approval (must be a signatory)signingCallback: Callback function to sign the execution transactioneraPeriod: 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:
- 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);
}