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
+
+ + + +
+Implementation + + +
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
+    }
+}
+
+ + +
@@ -2124,6 +2182,38 @@ Create a multisig transaction, which will have one approval initially (from the + + + + +## Function `create_transaction_with_eviction` + +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<u8>)
+
+ + + +
+Implementation + + +
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);
+}
+
+ + +
@@ -2170,6 +2260,38 @@ to provide the full payload, which will be validated against the hash stored on- + + + + +## Function `create_transaction_with_hash_with_eviction` + +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<u8>)
+
+ + + +
+Implementation + + +
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);
+}
+
+ + +
@@ -2224,14 +2346,14 @@ Reject a multisig transaction. - + -## Function `vote_transanction` +## Function `vote_transaction` Generic function that can be used to either approve or reject a multisig transaction -
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