diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md index b787024d32e31..9bbe4e35689ae 100644 --- a/aptos-move/framework/aptos-framework/doc/multisig_account.md +++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md @@ -69,6 +69,7 @@ and implement the governance voting logic on top. - [Function `last_resolved_sequence_number`](#0x1_multisig_account_last_resolved_sequence_number) - [Function `next_sequence_number`](#0x1_multisig_account_next_sequence_number) - [Function `vote`](#0x1_multisig_account_vote) +- [Function `available_transaction_queue_capacity`](#0x1_multisig_account_available_transaction_queue_capacity) - [Function `create_with_existing_account`](#0x1_multisig_account_create_with_existing_account) - [Function `create_with_existing_account_and_revoke_auth_key`](#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key) - [Function `create`](#0x1_multisig_account_create) @@ -87,11 +88,18 @@ and implement the governance voting logic on top. - [Function `update_metadata`](#0x1_multisig_account_update_metadata) - [Function `update_metadata_internal`](#0x1_multisig_account_update_metadata_internal) - [Function `create_transaction`](#0x1_multisig_account_create_transaction) +- [Function `create_transaction_with_eviction`](#0x1_multisig_account_create_transaction_with_eviction) - [Function `create_transaction_with_hash`](#0x1_multisig_account_create_transaction_with_hash) +- [Function `create_transaction_with_hash_with_eviction`](#0x1_multisig_account_create_transaction_with_hash_with_eviction) - [Function `approve_transaction`](#0x1_multisig_account_approve_transaction) - [Function `reject_transaction`](#0x1_multisig_account_reject_transaction) +- [Function `vote_transaction`](#0x1_multisig_account_vote_transaction) - [Function `vote_transanction`](#0x1_multisig_account_vote_transanction) +- [Function `vote_transactions`](#0x1_multisig_account_vote_transactions) +- [Function `vote_all_transactions`](#0x1_multisig_account_vote_all_transactions) - [Function `execute_rejected_transaction`](#0x1_multisig_account_execute_rejected_transaction) +- [Function `execute_rejected_transactions`](#0x1_multisig_account_execute_rejected_transactions) +- [Function `execute_all_rejected_transactions`](#0x1_multisig_account_execute_all_rejected_transactions) - [Function `validate_multisig_transaction`](#0x1_multisig_account_validate_multisig_transaction) - [Function `successful_transaction_execution_cleanup`](#0x1_multisig_account_successful_transaction_execution_cleanup) - [Function `failed_transaction_execution_cleanup`](#0x1_multisig_account_failed_transaction_execution_cleanup) @@ -884,6 +892,16 @@ Number of signatures required must be more than zero and at most the total numbe + + +The number of pending transactions has exceeded the maximum allowed. + + +
const EMAX_PENDING_TRANSACTIONS_EXCEEDED: u64 = 19;
+
+
+
+
Multisig accounts has not been enabled on this current network yet.
@@ -994,6 +1012,15 @@ Transaction with specified id cannot be found.
+
+
+
+
+const MAX_PENDING_TRANSACTIONS: u64 = 10;
+
+
+
+
## Function `metadata`
@@ -1361,6 +1388,37 @@ Return a bool tuple indicating whether an owner has voted and if so, whether the
+
+
+
+
+## Function `available_transaction_queue_capacity`
+
+
+
+#[view]
+public fun available_transaction_queue_capacity(multisig_account: address): u64
+
+
+
+
+public fun available_transaction_queue_capacity(multisig_account: address): u64 acquires MultisigAccount {
+ let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account);
+ let num_pending_transactions = multisig_account_resource.next_sequence_number - multisig_account_resource.last_executed_sequence_number - 1;
+ if (num_pending_transactions > MAX_PENDING_TRANSACTIONS) {
+ 0
+ } else {
+ MAX_PENDING_TRANSACTIONS - num_pending_transactions
+ }
+}
+
+
+
+
public entry fun create_transaction_with_eviction(owner: &signer, multisig_account: address, payload: vector<u8>)
+
+
+
+
+public entry fun create_transaction_with_eviction(
+ owner: &signer,
+ multisig_account: address,
+ payload: vector<u8>,
+) acquires MultisigAccount {
+ while(available_transaction_queue_capacity(multisig_account) == 0) {
+ execute_rejected_transaction(owner, multisig_account)
+ };
+ create_transaction(owner, multisig_account, payload);
+}
+
+
+
+
public entry fun create_transaction_with_hash_with_eviction(owner: &signer, multisig_account: address, payload_hash: vector<u8>)
+
+
+
+
+public entry fun create_transaction_with_hash_with_eviction(
+ owner: &signer,
+ multisig_account: address,
+ payload_hash: vector<u8>,
+) acquires MultisigAccount {
+ while(available_transaction_queue_capacity(multisig_account) == 0) {
+ execute_rejected_transaction(owner, multisig_account)
+ };
+ create_transaction_with_hash(owner, multisig_account, payload_hash);
+}
+
+
+
+
public entry fun vote_transanction(owner: &signer, multisig_account: address, sequence_number: u64, approved: bool)
+public entry fun vote_transaction(owner: &signer, multisig_account: address, sequence_number: u64, approved: bool)
@@ -2240,7 +2362,7 @@ Generic function that can be used to either approve or reject a multisig transac
Implementation
-public entry fun vote_transanction(
+public entry fun vote_transaction(
owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
assert_multisig_account_exists(multisig_account);
let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account);
@@ -2273,6 +2395,91 @@ Generic function that can be used to either approve or reject a multisig transac
+
+
+
+
+## Function `vote_transanction`
+
+Retained for backward compatibility: the function with the typographical error in its name
+will continue to be an accessible entry point.
+
+
+public entry fun vote_transanction(owner: &signer, multisig_account: address, sequence_number: u64, approved: bool)
+
+
+
+
+
+Implementation
+
+
+public entry fun vote_transanction(
+ owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
+ vote_transaction(owner, multisig_account, sequence_number, approved);
+}
+
+
+
+
+
+
+
+
+## Function `vote_transactions`
+
+Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+
+
+public entry fun vote_transactions(owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool)
+
+
+
+
+
+Implementation
+
+
+public entry fun vote_transactions(
+ owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool) acquires MultisigAccount {
+ let sequence_number = starting_sequence_number;
+ while(sequence_number <= final_sequence_number) {
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+ sequence_number = sequence_number + 1;
+ }
+}
+
+
+
+
+
+
+
+
+## Function `vote_all_transactions`
+
+Generic function that can be used to either approve or reject all pending transactions.
+
+
+public entry fun vote_all_transactions(owner: &signer, multisig_account: address, approved: bool)
+
+
+
+
+
+Implementation
+
+
+public entry fun vote_all_transactions(
+ owner: &signer, multisig_account: address, approved: bool) acquires MultisigAccount {
+ let starting_sequence_number = last_resolved_sequence_number(multisig_account) + 1;
+ let final_sequence_number = next_sequence_number(multisig_account) - 1;
+ vote_transactions(owner, multisig_account, starting_sequence_number, final_sequence_number, approved);
+}
+
+
+
+
@@ -2322,6 +2529,67 @@ Remove the next transaction if it has sufficient owner rejections.
+
+
+
+
+## Function `execute_rejected_transactions`
+
+Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+
+
+public entry fun execute_rejected_transactions(owner: &signer, multisig_account: address, final_sequence_number: u64)
+
+
+
+
+
+Implementation
+
+
+public entry fun execute_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+ final_sequence_number: u64,
+) acquires MultisigAccount {
+ assert!(last_resolved_sequence_number(multisig_account) < final_sequence_number, error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ assert!(final_sequence_number < next_sequence_number(multisig_account), error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ while(last_resolved_sequence_number(multisig_account) < final_sequence_number) {
+ execute_rejected_transaction(owner, multisig_account);
+ }
+}
+
+
+
+
+
+
+
+
+## Function `execute_all_rejected_transactions`
+
+Remove all pending transactions if they have sufficient owner rejections.
+
+
+public entry fun execute_all_rejected_transactions(owner: &signer, multisig_account: address)
+
+
+
+
+
+Implementation
+
+
+public entry fun execute_all_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+) acquires MultisigAccount {
+ execute_rejected_transactions(owner, multisig_account, next_sequence_number(multisig_account) - 1);
+}
+
+
+
+
@@ -2501,6 +2769,12 @@ This function is private so no other code can call this beside the VM itself as
fun add_transaction(creator: address, multisig_account: &mut MultisigAccount, transaction: MultisigTransaction) {
+ let num_pending_transactions = multisig_account.next_sequence_number - (multisig_account.last_executed_sequence_number + 1);
+ assert!(
+ num_pending_transactions < MAX_PENDING_TRANSACTIONS,
+ error::invalid_state(EMAX_PENDING_TRANSACTIONS_EXCEEDED)
+ );
+
// The transaction creator also automatically votes for the transaction.
simple_map::add(&mut transaction.votes, creator, true);
diff --git a/aptos-move/framework/aptos-framework/sources/multisig_account.move b/aptos-move/framework/aptos-framework/sources/multisig_account.move
index 6c3778c870585..fafa0ec4a6e63 100644
--- a/aptos-move/framework/aptos-framework/sources/multisig_account.move
+++ b/aptos-move/framework/aptos-framework/sources/multisig_account.move
@@ -93,9 +93,13 @@ module aptos_framework::multisig_account {
const EINVALID_SEQUENCE_NUMBER: u64 = 17;
/// Provided owners to remove and new owners overlap.
const EOWNERS_TO_REMOVE_NEW_OWNERS_OVERLAP: u64 = 18;
+ /// The number of pending transactions has exceeded the maximum allowed.
+ const EMAX_PENDING_TRANSACTIONS_EXCEEDED: u64 = 19;
const ZERO_AUTH_KEY: vector = x"0000000000000000000000000000000000000000000000000000000000000000";
+ const MAX_PENDING_TRANSACTIONS: u64 = 10;
+
/// Represents a multisig account's configurations and transactions.
/// This will be stored in the multisig account (created as a resource account separate from any owner accounts).
struct MultisigAccount has key {
@@ -384,6 +388,17 @@ module aptos_framework::multisig_account {
(voted, vote)
}
+ #[view]
+ public fun available_transaction_queue_capacity(multisig_account: address): u64 acquires MultisigAccount {
+ let multisig_account_resource = borrow_global_mut(multisig_account);
+ let num_pending_transactions = multisig_account_resource.next_sequence_number - multisig_account_resource.last_executed_sequence_number - 1;
+ if (num_pending_transactions > MAX_PENDING_TRANSACTIONS) {
+ 0
+ } else {
+ MAX_PENDING_TRANSACTIONS - num_pending_transactions
+ }
+ }
+
////////////////////////// Multisig account creation functions ///////////////////////////////
/// Creates a new multisig account on top of an existing account.
@@ -793,6 +808,18 @@ module aptos_framework::multisig_account {
add_transaction(creator, multisig_account_resource, transaction);
}
+ /// Create a multisig transaction with eviction if the transaction queue is full.
+ public entry fun create_transaction_with_eviction(
+ owner: &signer,
+ multisig_account: address,
+ payload: vector,
+ ) acquires MultisigAccount {
+ while(available_transaction_queue_capacity(multisig_account) == 0) {
+ execute_rejected_transaction(owner, multisig_account)
+ };
+ create_transaction(owner, multisig_account, payload);
+ }
+
/// Create a multisig transaction with a transaction hash instead of the full payload.
/// This means the payload will be stored off chain for gas saving. Later, during execution, the executor will need
/// to provide the full payload, which will be validated against the hash stored on-chain.
@@ -819,6 +846,18 @@ module aptos_framework::multisig_account {
add_transaction(creator, multisig_account_resource, transaction);
}
+ /// Create a multisig transaction with a transaction hash while evicting the next transaction if the queue is full.
+ public entry fun create_transaction_with_hash_with_eviction(
+ owner: &signer,
+ multisig_account: address,
+ payload_hash: vector,
+ ) acquires MultisigAccount {
+ while(available_transaction_queue_capacity(multisig_account) == 0) {
+ execute_rejected_transaction(owner, multisig_account)
+ };
+ create_transaction_with_hash(owner, multisig_account, payload_hash);
+ }
+
/// Approve a multisig transaction.
public entry fun approve_transaction(
owner: &signer, multisig_account: address, sequence_number: u64) acquires MultisigAccount {
@@ -832,7 +871,7 @@ module aptos_framework::multisig_account {
}
/// Generic function that can be used to either approve or reject a multisig transaction
- public entry fun vote_transanction(
+ public entry fun vote_transaction(
owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
assert_multisig_account_exists(multisig_account);
let multisig_account_resource = borrow_global_mut(multisig_account);
@@ -862,6 +901,31 @@ module aptos_framework::multisig_account {
);
}
+ /// Retained for backward compatibility: the function with the typographical error in its name
+ /// will continue to be an accessible entry point.
+ public entry fun vote_transanction(
+ owner: &signer, multisig_account: address, sequence_number: u64, approved: bool) acquires MultisigAccount {
+ vote_transaction(owner, multisig_account, sequence_number, approved);
+ }
+
+ /// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+ public entry fun vote_transactions(
+ owner: &signer, multisig_account: address, starting_sequence_number: u64, final_sequence_number: u64, approved: bool) acquires MultisigAccount {
+ let sequence_number = starting_sequence_number;
+ while(sequence_number <= final_sequence_number) {
+ vote_transanction(owner, multisig_account, sequence_number, approved);
+ sequence_number = sequence_number + 1;
+ }
+ }
+
+ /// Generic function that can be used to either approve or reject all pending transactions.
+ public entry fun vote_all_transactions(
+ owner: &signer, multisig_account: address, approved: bool) acquires MultisigAccount {
+ let starting_sequence_number = last_resolved_sequence_number(multisig_account) + 1;
+ let final_sequence_number = next_sequence_number(multisig_account) - 1;
+ vote_transactions(owner, multisig_account, starting_sequence_number, final_sequence_number, approved);
+ }
+
/// Remove the next transaction if it has sufficient owner rejections.
public entry fun execute_rejected_transaction(
owner: &signer,
@@ -891,6 +955,27 @@ module aptos_framework::multisig_account {
);
}
+ /// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+ public entry fun execute_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+ final_sequence_number: u64,
+ ) acquires MultisigAccount {
+ assert!(last_resolved_sequence_number(multisig_account) < final_sequence_number, error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ assert!(final_sequence_number < next_sequence_number(multisig_account), error::invalid_argument(EINVALID_SEQUENCE_NUMBER));
+ while(last_resolved_sequence_number(multisig_account) < final_sequence_number) {
+ execute_rejected_transaction(owner, multisig_account);
+ }
+ }
+
+ /// Remove all pending transactions if they have sufficient owner rejections.
+ public entry fun execute_all_rejected_transactions(
+ owner: &signer,
+ multisig_account: address,
+ ) acquires MultisigAccount {
+ execute_rejected_transactions(owner, multisig_account, next_sequence_number(multisig_account) - 1);
+ }
+
////////////////////////// To be called by VM only ///////////////////////////////
/// Called by the VM as part of transaction prologue, which is invoked during mempool transaction validation and as
@@ -978,6 +1063,12 @@ module aptos_framework::multisig_account {
}
fun add_transaction(creator: address, multisig_account: &mut MultisigAccount, transaction: MultisigTransaction) {
+ let num_pending_transactions = multisig_account.next_sequence_number - (multisig_account.last_executed_sequence_number + 1);
+ assert!(
+ num_pending_transactions < MAX_PENDING_TRANSACTIONS,
+ error::invalid_state(EMAX_PENDING_TRANSACTIONS_EXCEEDED)
+ );
+
// The transaction creator also automatically votes for the transaction.
simple_map::add(&mut transaction.votes, creator, true);
@@ -1878,4 +1969,80 @@ module aptos_framework::multisig_account {
option::none()
);
}
+
+ #[test(owner_1 = @0x123, owner_2 = @0x124, owner_3 = @0x125)]
+ #[expected_failure(abort_code = 196627, location = Self)]
+ fun test_max_pending_transaction_limit_should_fail(
+ owner_1: &signer,
+ owner_2: &signer,
+ owner_3: &signer
+ ) acquires MultisigAccount {
+ setup();
+ let owner_1_addr = address_of(owner_1);
+ let owner_2_addr = address_of(owner_2);
+ let owner_3_addr = address_of(owner_3);
+ create_account(owner_1_addr);
+ let multisig_address = get_next_multisig_account_address(owner_1_addr);
+ create_with_owners(
+ owner_1,
+ vector[owner_2_addr, owner_3_addr],
+ 2,
+ vector[],
+ vector[]
+ );
+
+ let remaining_iterations = MAX_PENDING_TRANSACTIONS + 1;
+ while (remaining_iterations > 0) {
+ create_transaction(owner_1, multisig_address, PAYLOAD);
+ remaining_iterations = remaining_iterations - 1;
+ }
+ }
+
+ #[test(owner_1 = @0x123, owner_2 = @0x124, owner_3 = @0x125)]
+ fun test_dos_mitigation_end_to_end(
+ owner_1: &signer,
+ owner_2: &signer,
+ owner_3: &signer
+ ) acquires MultisigAccount {
+ setup();
+ let owner_1_addr = address_of(owner_1);
+ let owner_2_addr = address_of(owner_2);
+ let owner_3_addr = address_of(owner_3);
+ create_account(owner_1_addr);
+ let multisig_address = get_next_multisig_account_address(owner_1_addr);
+ create_with_owners(
+ owner_1,
+ vector[owner_2_addr, owner_3_addr],
+ 2,
+ vector[],
+ vector[]
+ );
+
+ // owner_3 is compromised and creates a bunch of bogus transactions.
+ let remaining_iterations = MAX_PENDING_TRANSACTIONS;
+ while (remaining_iterations > 0) {
+ create_transaction(owner_3, multisig_address, PAYLOAD);
+ remaining_iterations = remaining_iterations - 1;
+ };
+
+ // No one can create a transaction anymore because the transaction queue is full.
+ assert!(available_transaction_queue_capacity(multisig_address) == 0, 0);
+
+ // owner_1 and owner_2 vote "no" on all transactions.
+ vote_all_transactions(owner_1, multisig_address, false);
+ vote_all_transactions(owner_2, multisig_address, false);
+
+ // owner_1 evicts a transaction and creates a transaction to remove the compromised owner.
+ // Note that `PAYLOAD` is a placeholder and is not actually executed in this unit test.
+ create_transaction_with_eviction(owner_1, multisig_address, PAYLOAD);
+
+ // owner_2 approves the eviction transaction.
+ approve_transaction(owner_2, multisig_address, 11);
+
+ // owner_1 flushes the transaction queue except for the eviction transaction.
+ execute_rejected_transactions(owner_1, multisig_address, 10);
+
+ // execute the eviction transaction to remove the compromised owner.
+ assert!(can_be_executed(multisig_address, 11), 0);
+ }
}
diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
index 045a27c1a664a..48889f54270d5 100644
--- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
+++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
@@ -430,6 +430,12 @@ pub enum EntryFunctionCall {
payload: Vec,
},
+ /// Create a multisig transaction with eviction if the transaction queue is full.
+ MultisigAccountCreateTransactionWithEviction {
+ multisig_account: AccountAddress,
+ payload: Vec,
+ },
+
/// Create a multisig transaction with a transaction hash instead of the full payload.
/// This means the payload will be stored off chain for gas saving. Later, during execution, the executor will need
/// to provide the full payload, which will be validated against the hash stored on-chain.
@@ -438,6 +444,12 @@ pub enum EntryFunctionCall {
payload_hash: Vec,
},
+ /// Create a multisig transaction with a transaction hash while evicting the next transaction if the queue is full.
+ MultisigAccountCreateTransactionWithHashWithEviction {
+ multisig_account: AccountAddress,
+ payload_hash: Vec,
+ },
+
/// Creates a new multisig account on top of an existing account.
///
/// This offers a migration path for an existing account with a multi-ed25519 auth key (native multisig account).
@@ -498,11 +510,22 @@ pub enum EntryFunctionCall {
metadata_values: Vec>,
},
+ /// Remove all pending transactions if they have sufficient owner rejections.
+ MultisigAccountExecuteAllRejectedTransactions {
+ multisig_account: AccountAddress,
+ },
+
/// Remove the next transaction if it has sufficient owner rejections.
MultisigAccountExecuteRejectedTransaction {
multisig_account: AccountAddress,
},
+ /// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+ MultisigAccountExecuteRejectedTransactions {
+ multisig_account: AccountAddress,
+ final_sequence_number: u64,
+ },
+
/// Reject a multisig transaction.
MultisigAccountRejectTransaction {
multisig_account: AccountAddress,
@@ -566,7 +589,29 @@ pub enum EntryFunctionCall {
new_num_signatures_required: u64,
},
+ /// Generic function that can be used to either approve or reject all pending transactions.
+ MultisigAccountVoteAllTransactions {
+ multisig_account: AccountAddress,
+ approved: bool,
+ },
+
/// Generic function that can be used to either approve or reject a multisig transaction
+ MultisigAccountVoteTransaction {
+ multisig_account: AccountAddress,
+ sequence_number: u64,
+ approved: bool,
+ },
+
+ /// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+ MultisigAccountVoteTransactions {
+ multisig_account: AccountAddress,
+ starting_sequence_number: u64,
+ final_sequence_number: u64,
+ approved: bool,
+ },
+
+ /// Retained for backward compatibility: the function with the typographical error in its name
+ /// will continue to be an accessible entry point.
MultisigAccountVoteTransanction {
multisig_account: AccountAddress,
sequence_number: u64,
@@ -1150,10 +1195,21 @@ impl EntryFunctionCall {
multisig_account,
payload,
} => multisig_account_create_transaction(multisig_account, payload),
+ MultisigAccountCreateTransactionWithEviction {
+ multisig_account,
+ payload,
+ } => multisig_account_create_transaction_with_eviction(multisig_account, payload),
MultisigAccountCreateTransactionWithHash {
multisig_account,
payload_hash,
} => multisig_account_create_transaction_with_hash(multisig_account, payload_hash),
+ MultisigAccountCreateTransactionWithHashWithEviction {
+ multisig_account,
+ payload_hash,
+ } => multisig_account_create_transaction_with_hash_with_eviction(
+ multisig_account,
+ payload_hash,
+ ),
MultisigAccountCreateWithExistingAccount {
multisig_address,
owners,
@@ -1214,9 +1270,19 @@ impl EntryFunctionCall {
metadata_keys,
metadata_values,
),
+ MultisigAccountExecuteAllRejectedTransactions { multisig_account } => {
+ multisig_account_execute_all_rejected_transactions(multisig_account)
+ },
MultisigAccountExecuteRejectedTransaction { multisig_account } => {
multisig_account_execute_rejected_transaction(multisig_account)
},
+ MultisigAccountExecuteRejectedTransactions {
+ multisig_account,
+ final_sequence_number,
+ } => multisig_account_execute_rejected_transactions(
+ multisig_account,
+ final_sequence_number,
+ ),
MultisigAccountRejectTransaction {
multisig_account,
sequence_number,
@@ -1250,6 +1316,26 @@ impl EntryFunctionCall {
MultisigAccountUpdateSignaturesRequired {
new_num_signatures_required,
} => multisig_account_update_signatures_required(new_num_signatures_required),
+ MultisigAccountVoteAllTransactions {
+ multisig_account,
+ approved,
+ } => multisig_account_vote_all_transactions(multisig_account, approved),
+ MultisigAccountVoteTransaction {
+ multisig_account,
+ sequence_number,
+ approved,
+ } => multisig_account_vote_transaction(multisig_account, sequence_number, approved),
+ MultisigAccountVoteTransactions {
+ multisig_account,
+ starting_sequence_number,
+ final_sequence_number,
+ approved,
+ } => multisig_account_vote_transactions(
+ multisig_account,
+ starting_sequence_number,
+ final_sequence_number,
+ approved,
+ ),
MultisigAccountVoteTransanction {
multisig_account,
sequence_number,
@@ -2581,6 +2667,28 @@ pub fn multisig_account_create_transaction(
))
}
+/// Create a multisig transaction with eviction if the transaction queue is full.
+pub fn multisig_account_create_transaction_with_eviction(
+ multisig_account: AccountAddress,
+ payload: Vec,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("create_transaction_with_eviction").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&payload).unwrap(),
+ ],
+ ))
+}
+
/// Create a multisig transaction with a transaction hash instead of the full payload.
/// This means the payload will be stored off chain for gas saving. Later, during execution, the executor will need
/// to provide the full payload, which will be validated against the hash stored on-chain.
@@ -2605,6 +2713,28 @@ pub fn multisig_account_create_transaction_with_hash(
))
}
+/// Create a multisig transaction with a transaction hash while evicting the next transaction if the queue is full.
+pub fn multisig_account_create_transaction_with_hash_with_eviction(
+ multisig_account: AccountAddress,
+ payload_hash: Vec,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("create_transaction_with_hash_with_eviction").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&payload_hash).unwrap(),
+ ],
+ ))
+}
+
/// Creates a new multisig account on top of an existing account.
///
/// This offers a migration path for an existing account with a multi-ed25519 auth key (native multisig account).
@@ -2745,6 +2875,24 @@ pub fn multisig_account_create_with_owners_then_remove_bootstrapper(
))
}
+/// Remove all pending transactions if they have sufficient owner rejections.
+pub fn multisig_account_execute_all_rejected_transactions(
+ multisig_account: AccountAddress,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("execute_all_rejected_transactions").to_owned(),
+ vec![],
+ vec![bcs::to_bytes(&multisig_account).unwrap()],
+ ))
+}
+
/// Remove the next transaction if it has sufficient owner rejections.
pub fn multisig_account_execute_rejected_transaction(
multisig_account: AccountAddress,
@@ -2763,6 +2911,28 @@ pub fn multisig_account_execute_rejected_transaction(
))
}
+/// Remove the next transactions until the final_sequence_number if they have sufficient owner rejections.
+pub fn multisig_account_execute_rejected_transactions(
+ multisig_account: AccountAddress,
+ final_sequence_number: u64,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("execute_rejected_transactions").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&final_sequence_number).unwrap(),
+ ],
+ ))
+}
+
/// Reject a multisig transaction.
pub fn multisig_account_reject_transaction(
multisig_account: AccountAddress,
@@ -2942,7 +3112,80 @@ pub fn multisig_account_update_signatures_required(
))
}
+/// Generic function that can be used to either approve or reject all pending transactions.
+pub fn multisig_account_vote_all_transactions(
+ multisig_account: AccountAddress,
+ approved: bool,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("vote_all_transactions").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&approved).unwrap(),
+ ],
+ ))
+}
+
/// Generic function that can be used to either approve or reject a multisig transaction
+pub fn multisig_account_vote_transaction(
+ multisig_account: AccountAddress,
+ sequence_number: u64,
+ approved: bool,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("vote_transaction").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&sequence_number).unwrap(),
+ bcs::to_bytes(&approved).unwrap(),
+ ],
+ ))
+}
+
+/// Generic function that can be used to either approve or reject a batch of transactions within a specified range.
+pub fn multisig_account_vote_transactions(
+ multisig_account: AccountAddress,
+ starting_sequence_number: u64,
+ final_sequence_number: u64,
+ approved: bool,
+) -> TransactionPayload {
+ TransactionPayload::EntryFunction(EntryFunction::new(
+ ModuleId::new(
+ AccountAddress::new([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ ]),
+ ident_str!("multisig_account").to_owned(),
+ ),
+ ident_str!("vote_transactions").to_owned(),
+ vec![],
+ vec![
+ bcs::to_bytes(&multisig_account).unwrap(),
+ bcs::to_bytes(&starting_sequence_number).unwrap(),
+ bcs::to_bytes(&final_sequence_number).unwrap(),
+ bcs::to_bytes(&approved).unwrap(),
+ ],
+ ))
+}
+
+/// Retained for backward compatibility: the function with the typographical error in its name
+/// will continue to be an accessible entry point.
pub fn multisig_account_vote_transanction(
multisig_account: AccountAddress,
sequence_number: u64,
@@ -4725,6 +4968,21 @@ mod decoder {
}
}
+ pub fn multisig_account_create_transaction_with_eviction(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(
+ EntryFunctionCall::MultisigAccountCreateTransactionWithEviction {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ payload: bcs::from_bytes(script.args().get(1)?).ok()?,
+ },
+ )
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_create_transaction_with_hash(
payload: &TransactionPayload,
) -> Option {
@@ -4740,6 +4998,21 @@ mod decoder {
}
}
+ pub fn multisig_account_create_transaction_with_hash_with_eviction(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(
+ EntryFunctionCall::MultisigAccountCreateTransactionWithHashWithEviction {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ payload_hash: bcs::from_bytes(script.args().get(1)?).ok()?,
+ },
+ )
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_create_with_existing_account(
payload: &TransactionPayload,
) -> Option {
@@ -4816,6 +5089,20 @@ mod decoder {
}
}
+ pub fn multisig_account_execute_all_rejected_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(
+ EntryFunctionCall::MultisigAccountExecuteAllRejectedTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ },
+ )
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_execute_rejected_transaction(
payload: &TransactionPayload,
) -> Option {
@@ -4830,6 +5117,21 @@ mod decoder {
}
}
+ pub fn multisig_account_execute_rejected_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(
+ EntryFunctionCall::MultisigAccountExecuteRejectedTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ final_sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ },
+ )
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_reject_transaction(
payload: &TransactionPayload,
) -> Option {
@@ -4930,6 +5232,48 @@ mod decoder {
}
}
+ pub fn multisig_account_vote_all_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(EntryFunctionCall::MultisigAccountVoteAllTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ approved: bcs::from_bytes(script.args().get(1)?).ok()?,
+ })
+ } else {
+ None
+ }
+ }
+
+ pub fn multisig_account_vote_transaction(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(EntryFunctionCall::MultisigAccountVoteTransaction {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ approved: bcs::from_bytes(script.args().get(2)?).ok()?,
+ })
+ } else {
+ None
+ }
+ }
+
+ pub fn multisig_account_vote_transactions(
+ payload: &TransactionPayload,
+ ) -> Option {
+ if let TransactionPayload::EntryFunction(script) = payload {
+ Some(EntryFunctionCall::MultisigAccountVoteTransactions {
+ multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?,
+ starting_sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?,
+ final_sequence_number: bcs::from_bytes(script.args().get(2)?).ok()?,
+ approved: bcs::from_bytes(script.args().get(3)?).ok()?,
+ })
+ } else {
+ None
+ }
+ }
+
pub fn multisig_account_vote_transanction(
payload: &TransactionPayload,
) -> Option {
@@ -5831,10 +6175,18 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy