Module 0x1::transaction_limits
Manages configuration and validation for higher transaction limits based on staking.
Users can request multipliers to transaction limits (e..g, execution limit or IO limit) if they prove they control a significant stake:
- as a stake pool owner,
- as a delegated voter,
- as a delegation pool delegator. For example, one can request 2.5x on execution limits and 5x on IO limits. Multipliers are in basis points where 1 maps to 100, to support fractions.
The on-chain config stores a vector of tiers. Each tier maps multiplier to the required minimum stake threshold. A smallest multiplier that is greater than or equal to the requested multiplier is chosen.
- Struct
TxnLimitTier - Enum Resource
TxnLimitsConfig - Enum
RequestedMultipliers - Enum
UserTxnLimitsRequest - Constants
- Function
new_tier - Function
validate_tiers - Function
new_tiers - Function
find_min_stake_required - Function
initialize - Function
update_config - Function
validate_enough_stake - Function
validate_high_txn_limits - Specification
use 0x1::aptos_governance;
use 0x1::delegation_pool;
use 0x1::error;
use 0x1::stake;
use 0x1::system_addresses;
Struct TxnLimitTier
A single tier: the minimum committed stake required and the multiplier it unlocks.
struct TxnLimitTier has copy, drop, store
Fields
-
min_stake: u64 -
multiplier_bps: u64
Enum Resource TxnLimitsConfig
On-chain configuration for higher transaction limits. Stores a vector of tiers for each dimension (e.g., execution, IO). Tiers are ordered monotonically by both minimum stakes and multipliers.
enum TxnLimitsConfig has key
Variants
V1
Fields
-
execution_tiers: vector<transaction_limits::TxnLimitTier> -
io_tiers: vector<transaction_limits::TxnLimitTier>
Enum RequestedMultipliers
Multipliers requested by the user, expressed in basis points (That is, 1x is 100, 2.5x is 250).
INVARIANT: must match Rust enum for BCS serialization.
enum RequestedMultipliers has copy, drop, store
Variants
V1
Fields
-
execution_bps: u64 -
io_bps: u64
Enum UserTxnLimitsRequest
Request for higher transaction limits, passed to the prologue. Carries the proof that the sender has enough stake.
INVARIANT: must match Rust enum for BCS serialization.
enum UserTxnLimitsRequest has copy, drop
Variants
StakePoolOwner
Fields
-
multipliers: transaction_limits::RequestedMultipliers
DelegatedVoter
Fields
-
pool_address: address -
multipliers: transaction_limits::RequestedMultipliers
DelegationPoolDelegator
Fields
-
pool_address: address -
multipliers: transaction_limits::RequestedMultipliers
Constants
Fee payer is not the delegated voter of the specified stake pool.
const ENOT_DELEGATED_VOTER: u64 = 3;
No delegation pool exists at the specified address.
const EDELEGATION_POOL_NOT_FOUND: u64 = 4;
Committed stake is insufficient for the requested multiplier tier.
const EINSUFFICIENT_STAKE: u64 = 5;
Multiplier must be > 100 bps (> 1x).
const EINVALID_MULTIPLIER: u64 = 7;
Requested multiplier is not available in any configured tier.
const EMULTIPLIER_NOT_AVAILABLE: u64 = 8;
Fee payer is not the owner of the specified stake pool.
const ENOT_STAKE_POOL_OWNER: u64 = 2;
No stake pool exists at the specified address.
const ESTAKE_POOL_NOT_FOUND: u64 = 1;
Config tiers are not monotonically ordered.
const ETHRESHOLDS_NOT_MONOTONIC: u64 = 6;
Min-stakes and multipliers vectors have different lengths.
const EVECTOR_LENGTH_MISMATCH: u64 = 9;
Every multiplier must be less than or equal to this maximum (100x).
INVARIANT: must match Rust version checked by VM.
const MAX_MULTIPLIER_BPS: u64 = 10000;
Every multiplier must be greater than this minimum (1x).
INVARIANT: must match Rust version checked by VM.
const MIN_MULTIPLIER_BPS: u64 = 100;
Function new_tier
Creates a new tier. Aborts if multiplier is not in (100, 10000] bps.
public fun new_tier(min_stake: u64, multiplier_bps: u64): transaction_limits::TxnLimitTier
Implementation
public fun new_tier(min_stake: u64, multiplier_bps: u64): TxnLimitTier {
assert!(
multiplier_bps > MIN_MULTIPLIER_BPS && multiplier_bps <= MAX_MULTIPLIER_BPS,
error::invalid_argument(EINVALID_MULTIPLIER)
);
TxnLimitTier { min_stake, multiplier_bps }
}
Function validate_tiers
Aborts if:
- Minimum stake tiers are not monotonically increasing.
- Multiplier tiers are not strictly monotonically increasing.
fun validate_tiers(tiers: &vector<transaction_limits::TxnLimitTier>)
Implementation
fun validate_tiers(tiers: &vector<TxnLimitTier>) {
let i = 1;
let len = tiers.length();
while (i < len) {
let prev = &tiers[i - 1];
let curr = &tiers[i];
assert!(
curr.min_stake >= prev.min_stake
&& curr.multiplier_bps > prev.multiplier_bps,
error::invalid_argument(ETHRESHOLDS_NOT_MONOTONIC)
);
i += 1;
};
}
Function new_tiers
Builds a vector of tiers from inputs.
Aborts if:
- Minimum stakes and multipliers vectors have different lengths.
- Minimum stakes and multipliers vectors are not monotonically increasing.
- Multiplier is not valid (1x or below).
fun new_tiers(min_stakes: vector<u64>, multipliers_bps: vector<u64>): vector<transaction_limits::TxnLimitTier>
Implementation
fun new_tiers(min_stakes: vector<u64>, multipliers_bps: vector<u64>)
: vector<TxnLimitTier> {
let len = min_stakes.length();
assert!(
len == multipliers_bps.length(),
error::invalid_argument(EVECTOR_LENGTH_MISMATCH)
);
let tiers = vector[];
let i = 0;
while (i < len) {
tiers.push_back(new_tier(min_stakes[i], multipliers_bps[i]));
i += 1;
};
validate_tiers(&tiers);
tiers
}
Function find_min_stake_required
Finds the smallest tier whose multiplier is greater than or equal to the requested multiplier. Returns minimum stake correspondng to this tier.
Aborts if no tier can cover the request.
fun find_min_stake_required(tiers: &vector<transaction_limits::TxnLimitTier>, multiplier_bps: u64): u64
Implementation
fun find_min_stake_required(
tiers: &vector<TxnLimitTier>, multiplier_bps: u64
): u64 {
let (found, i) = tiers.find(|t| t.multiplier_bps >= multiplier_bps);
assert!(found, error::invalid_argument(EMULTIPLIER_NOT_AVAILABLE));
tiers[i].min_stake
}
Function initialize
Only called during genesis.
public(friend) fun initialize(aptos_framework: &signer, execution_tiers: vector<transaction_limits::TxnLimitTier>, io_tiers: vector<transaction_limits::TxnLimitTier>)
Implementation
friend fun initialize(
aptos_framework: &signer,
execution_tiers: vector<TxnLimitTier>,
io_tiers: vector<TxnLimitTier>
) {
system_addresses::assert_aptos_framework(aptos_framework);
validate_tiers(&execution_tiers);
validate_tiers(&io_tiers);
move_to(
aptos_framework,
TxnLimitsConfig::V1 { execution_tiers, io_tiers }
);
}
Function update_config
Governance-only: update stake thresholds and multipliers.
public fun update_config(aptos_framework: &signer, execution_min_stakes: vector<u64>, execution_multipliers_bps: vector<u64>, io_min_stakes: vector<u64>, io_multipliers_bps: vector<u64>)
Implementation
public fun update_config(
aptos_framework: &signer,
execution_min_stakes: vector<u64>,
execution_multipliers_bps: vector<u64>,
io_min_stakes: vector<u64>,
io_multipliers_bps: vector<u64>
) acquires TxnLimitsConfig {
system_addresses::assert_aptos_framework(aptos_framework);
let execution_tiers = new_tiers(
execution_min_stakes, execution_multipliers_bps
);
let io_tiers = new_tiers(io_min_stakes, io_multipliers_bps);
if (!exists<TxnLimitsConfig>(@aptos_framework)) {
move_to(
aptos_framework,
TxnLimitsConfig::V1 { execution_tiers, io_tiers }
);
} else {
let config = &mut TxnLimitsConfig[@aptos_framework];
config.execution_tiers = execution_tiers;
config.io_tiers = io_tiers;
}
}
Function validate_enough_stake
Aborts if:
- Requested multipliers are not well-formed.
- Transaction limits config does not exist or there is no tier matching the requested multipliers.
- There is not enough stake to cover the minimum required amount.
fun validate_enough_stake(stake_amount: u64, multipliers: transaction_limits::RequestedMultipliers)
Implementation
fun validate_enough_stake(
stake_amount: u64, multipliers: RequestedMultipliers
) acquires TxnLimitsConfig {
let (execution_bps, io_bps) =
match(multipliers) {
RequestedMultipliers::V1 { execution_bps, io_bps } => (execution_bps, io_bps)
};
assert!(
execution_bps > MIN_MULTIPLIER_BPS && execution_bps <= MAX_MULTIPLIER_BPS,
error::invalid_argument(EINVALID_MULTIPLIER)
);
assert!(
io_bps > MIN_MULTIPLIER_BPS && io_bps <= MAX_MULTIPLIER_BPS,
error::invalid_argument(EINVALID_MULTIPLIER)
);
let config = &TxnLimitsConfig[@aptos_framework];
let execution_threshold =
find_min_stake_required(&config.execution_tiers, execution_bps);
let io_threshold = find_min_stake_required(&config.io_tiers, io_bps);
assert!(
stake_amount >= execution_threshold,
error::permission_denied(EINSUFFICIENT_STAKE)
);
assert!(
stake_amount >= io_threshold, error::permission_denied(EINSUFFICIENT_STAKE)
);
}
Function validate_high_txn_limits
Only called during prologue to validate that the fee payer qualifies for the requested limit multipliers.
public(friend) fun validate_high_txn_limits(fee_payer: address, request: transaction_limits::UserTxnLimitsRequest)
Implementation
friend fun validate_high_txn_limits(
fee_payer: address, request: UserTxnLimitsRequest
) acquires TxnLimitsConfig {
match(request) {
StakePoolOwner { multipliers } => {
assert!(
stake::owner_cap_exists(fee_payer),
error::permission_denied(ENOT_STAKE_POOL_OWNER)
);
let pool_address = stake::get_pool_address_for_owner(fee_payer);
let stake_amount = aptos_governance::get_voting_power(pool_address);
validate_enough_stake(stake_amount, multipliers);
},
DelegatedVoter { pool_address, multipliers } => {
assert!(
stake::stake_pool_exists(pool_address),
error::not_found(ESTAKE_POOL_NOT_FOUND)
);
assert!(
fee_payer == stake::get_delegated_voter(pool_address),
error::permission_denied(ENOT_DELEGATED_VOTER)
);
let stake_amount = aptos_governance::get_voting_power(pool_address);
validate_enough_stake(stake_amount, multipliers);
},
DelegationPoolDelegator { pool_address, multipliers } => {
assert!(
delegation_pool::delegation_pool_exists(pool_address),
error::not_found(EDELEGATION_POOL_NOT_FOUND)
);
let (active, _, pending_inactive) = delegation_pool::get_stake(
pool_address, fee_payer
);
validate_enough_stake(active + pending_inactive, multipliers);
}
}
}
Specification
pragma verify = false;