Module 0x1::transaction_validation
- Enum
ReplayProtector - Resource
TransactionValidation - Struct
GasPermission - Enum
PrologueArgs - Enum
EpilogueArgs - Constants
- Function
grant_gas_permission - Function
revoke_gas_permission - Function
initialize - Function
allow_missing_txn_authentication_key - Function
prologue_common - Function
check_for_replay_protection_regular_txn - Function
check_for_replay_protection_orderless_txn - Function
script_prologue - Function
script_prologue_extended - Function
multi_agent_script_prologue - Function
multi_agent_script_prologue_extended - Function
multi_agent_common_prologue - Function
fee_payer_script_prologue - Function
fee_payer_script_prologue_extended - Function
epilogue - Function
epilogue_extended - Function
epilogue_gas_payer - Function
epilogue_gas_payer_extended - Function
skip_auth_key_check - Function
skip_gas_payment - Function
unified_prologue - Function
unified_prologue_fee_payer - Function
unified_epilogue - Function
unified_prologue_v2 - Function
unified_prologue_fee_payer_v2 - Function
unified_epilogue_v2 - Function
versioned_prologue - Function
versioned_epilogue - Specification
- High-level Requirements
- Module-level Specification
- Function
grant_gas_permission - Function
revoke_gas_permission - Function
initialize - Function
prologue_common - Function
check_for_replay_protection_regular_txn - Function
check_for_replay_protection_orderless_txn - Function
script_prologue - Function
script_prologue_extended - Function
multi_agent_script_prologue - Function
multi_agent_script_prologue_extended - Function
multi_agent_common_prologue - Function
fee_payer_script_prologue - Function
fee_payer_script_prologue_extended - Function
epilogue - Function
epilogue_extended - Function
epilogue_gas_payer - Function
epilogue_gas_payer_extended - Function
unified_prologue - Function
unified_prologue_fee_payer - Function
unified_epilogue - Function
unified_prologue_v2 - Function
unified_prologue_fee_payer_v2 - Function
unified_epilogue_v2 - Function
versioned_prologue - Function
versioned_epilogue
use 0x1::account;
use 0x1::account_abstraction;
use 0x1::aptos_account;
use 0x1::chain_id;
use 0x1::create_signer;
use 0x1::error;
use 0x1::features;
use 0x1::nonce_validation;
use 0x1::option;
use 0x1::permissioned_signer;
use 0x1::signer;
use 0x1::system_addresses;
use 0x1::timestamp;
use 0x1::transaction_fee;
use 0x1::transaction_limits;
use 0x1::vector;
Enum ReplayProtector
enum ReplayProtector
Variants
Nonce
Fields
-
0: u64
SequenceNumber
Fields
-
0: u64
Resource TransactionValidation
This holds information that will be picked up by the VM to call the correct chain-specific prologue and epilogue functions
struct TransactionValidation has key
Fields
Struct GasPermission
struct GasPermission has copy, drop, store
Fields
-
dummy_field: bool
Enum PrologueArgs
Versioned enum-based prologue and epilogue
Arguments for versioned_prologue. A new field becomes a new enum variant.
- Old variants are kept for compatibility; their on-chain layout must remain stable.
- Only the most recent variant needs real handling here — old variants were executed against the framework version that shipped them and are not reached from this code.
enum PrologueArgs
Variants
V1
Fields
-
txn_sender_public_key: option::Option<vector<u8>> -
fee_payer_public_key_hash: option::Option<vector<u8>> -
replay_protector: transaction_validation::ReplayProtector -
secondary_signer_addresses: vector<address> -
secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>> -
txn_gas_price: u64 -
txn_max_gas_units: u64 -
txn_expiration_time: u64 -
chain_id: u8 -
is_simulation: bool -
txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>
Enum EpilogueArgs
Arguments for versioned_epilogue. A new field becomes a new enum variant.
- Old variants are kept for compatibility; their on-chain layout must remain stable.
- Only the most recent variant needs real handling here — old variants were executed against the framework version that shipped them and are not reached from this code.
enum EpilogueArgs
Variants
V1
Fields
-
fee_statement: transaction_fee::FeeStatement -
txn_gas_price: u64 -
txn_max_gas_units: u64 -
gas_units_remaining: u64 -
is_simulation: bool -
is_orderless_txn: bool
Constants
MSB is used to indicate a gas payer tx
const MAX_U64: u128 = 18446744073709551615;
Transaction exceeded its allocated max gas
const EOUT_OF_GAS: u64 = 6;
const MAX_EXP_TIME_SECONDS_FOR_ORDERLESS_TXNS: u64 = 100;
const PROLOGUE_EACCOUNT_DOES_NOT_EXIST: u64 = 1004;
const PROLOGUE_EBAD_CHAIN_ID: u64 = 1007;
const PROLOGUE_ECANT_PAY_GAS_DEPOSIT: u64 = 1005;
const PROLOGUE_EFEE_PAYER_NOT_ENABLED: u64 = 1010;
Prologue errors. These are separated out from the other errors in this module since they are mapped separately to major VM statuses, and are important to the semantics of the system.
const PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY: u64 = 1001;
const PROLOGUE_ENONCE_ALREADY_USED: u64 = 1012;
const PROLOGUE_ESECONDARY_KEYS_ADDRESSES_COUNT_MISMATCH: u64 = 1009;
const PROLOGUE_ESEQUENCE_NUMBER_TOO_BIG: u64 = 1008;
const PROLOGUE_ESEQUENCE_NUMBER_TOO_NEW: u64 = 1003;
const PROLOGUE_ESEQUENCE_NUMBER_TOO_OLD: u64 = 1002;
const PROLOGUE_ETRANSACTION_EXPIRATION_TOO_FAR_IN_FUTURE: u64 = 1013;
const PROLOGUE_ETRANSACTION_EXPIRED: u64 = 1006;
const PROLOGUE_PERMISSIONED_GAS_LIMIT_INSUFFICIENT: u64 = 1011;
Function grant_gas_permission
Permission management
Master signer grant permissioned signer ability to consume a given amount of gas in octas.
public fun grant_gas_permission(master: &signer, permissioned: &signer, gas_amount: u64)
Implementation
public fun grant_gas_permission(
master: &signer,
permissioned: &signer,
gas_amount: u64
) {
permissioned_signer::authorize_increase(
master,
permissioned,
(gas_amount as u256),
GasPermission {}
)
}
Function revoke_gas_permission
Removing permissions from permissioned signer.
public fun revoke_gas_permission(permissioned: &signer)
Implementation
public fun revoke_gas_permission(permissioned: &signer) {
permissioned_signer::revoke_permission(permissioned, GasPermission {})
}
Function initialize
Only called during genesis to initialize system resources for this module.
public(friend) fun initialize(aptos_framework: &signer, script_prologue_name: vector<u8>, module_prologue_name: vector<u8>, multi_agent_prologue_name: vector<u8>, user_epilogue_name: vector<u8>)
Implementation
public(friend) fun initialize(
aptos_framework: &signer,
script_prologue_name: vector<u8>,
// module_prologue_name is deprecated and not used.
module_prologue_name: vector<u8>,
multi_agent_prologue_name: vector<u8>,
user_epilogue_name: vector<u8>,
) {
system_addresses::assert_aptos_framework(aptos_framework);
move_to(aptos_framework, TransactionValidation {
module_addr: @aptos_framework,
module_name: b"transaction_validation",
script_prologue_name,
// module_prologue_name is deprecated and not used.
module_prologue_name,
multi_agent_prologue_name,
user_epilogue_name,
});
}
Function allow_missing_txn_authentication_key
fun allow_missing_txn_authentication_key(transaction_sender: address): bool
Implementation
inline fun allow_missing_txn_authentication_key(transaction_sender: address): bool {
// aa verifies authentication itself
features::is_derivable_account_abstraction_enabled()
|| (features::is_account_abstraction_enabled() && account_abstraction::using_dispatchable_authenticator(transaction_sender))
}
Function prologue_common
fun prologue_common(sender: &signer, gas_payer: &signer, replay_protector: transaction_validation::ReplayProtector, txn_authentication_key: option::Option<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
Implementation
fun prologue_common(
sender: &signer,
gas_payer: &signer,
replay_protector: ReplayProtector,
txn_authentication_key: Option<vector<u8>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
txn_limits_request: Option<UserTxnLimitsRequest>,
) {
let sender_address = signer::address_of(sender);
let gas_payer_address = signer::address_of(gas_payer);
assert!(
timestamp::now_seconds() < txn_expiration_time,
error::invalid_argument(PROLOGUE_ETRANSACTION_EXPIRED),
);
assert!(chain_id::get() == chain_id, error::invalid_argument(PROLOGUE_EBAD_CHAIN_ID));
// TODO[Orderless]: Here, we are maintaining the same order of validation steps as before orderless txns were introduced.
// Ideally, do the replay protection check in the end after the authentication key check and gas payment checks.
// Check if the authentication key is valid
if (!skip_auth_key_check(is_simulation, &txn_authentication_key)) {
if (txn_authentication_key.is_some()) {
if (
sender_address == gas_payer_address ||
account::exists_at(sender_address) ||
!features::sponsored_automatic_account_creation_enabled()
) {
assert!(
txn_authentication_key == option::some(account::get_authentication_key(sender_address)),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
);
};
} else {
assert!(
allow_missing_txn_authentication_key(sender_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
);
};
};
// Check for replay protection
match (replay_protector) {
SequenceNumber(txn_sequence_number) => {
check_for_replay_protection_regular_txn(
sender_address,
gas_payer_address,
txn_sequence_number,
);
},
Nonce(nonce) => {
check_for_replay_protection_orderless_txn(
sender_address,
nonce,
txn_expiration_time,
);
}
};
if (txn_limits_request.is_some()) {
transaction_limits::validate_high_txn_limits(
gas_payer_address,
txn_limits_request.destroy_some(),
);
};
// Check if the gas payer has enough balance to pay for the transaction
let max_transaction_fee = txn_gas_price * txn_max_gas_units;
if (!skip_gas_payment(
is_simulation,
gas_payer_address
)) {
assert!(
permissioned_signer::check_permission_capacity_above(
gas_payer,
(max_transaction_fee as u256),
GasPermission {}
),
error::permission_denied(PROLOGUE_PERMISSIONED_GAS_LIMIT_INSUFFICIENT)
);
assert!(
aptos_account::is_fungible_balance_at_least(gas_payer_address, max_transaction_fee),
error::invalid_argument(PROLOGUE_ECANT_PAY_GAS_DEPOSIT)
);
};
}
Function check_for_replay_protection_regular_txn
fun check_for_replay_protection_regular_txn(sender_address: address, gas_payer_address: address, txn_sequence_number: u64)
Implementation
fun check_for_replay_protection_regular_txn(
sender_address: address,
gas_payer_address: address,
txn_sequence_number: u64,
) {
if (
sender_address == gas_payer_address
|| account::exists_at(sender_address)
|| !features::sponsored_automatic_account_creation_enabled()
|| txn_sequence_number > 0
) {
assert!(account::exists_at(sender_address), error::invalid_argument(PROLOGUE_EACCOUNT_DOES_NOT_EXIST));
let account_sequence_number = account::get_sequence_number(sender_address);
assert!(
txn_sequence_number < (1u64 << 63),
error::out_of_range(PROLOGUE_ESEQUENCE_NUMBER_TOO_BIG)
);
assert!(
txn_sequence_number >= account_sequence_number,
error::invalid_argument(PROLOGUE_ESEQUENCE_NUMBER_TOO_OLD)
);
assert!(
txn_sequence_number == account_sequence_number,
error::invalid_argument(PROLOGUE_ESEQUENCE_NUMBER_TOO_NEW)
);
} else {
// In this case, the transaction is sponsored and the account does not exist, so ensure
// the default values match.
assert!(
txn_sequence_number == 0,
error::invalid_argument(PROLOGUE_ESEQUENCE_NUMBER_TOO_NEW)
);
};
}
Function check_for_replay_protection_orderless_txn
fun check_for_replay_protection_orderless_txn(sender: address, nonce: u64, txn_expiration_time: u64)
Implementation
fun check_for_replay_protection_orderless_txn(
sender: address,
nonce: u64,
txn_expiration_time: u64,
) {
// prologue_common already checks that the current_time > txn_expiration_time
assert!(
txn_expiration_time <= timestamp::now_seconds() + MAX_EXP_TIME_SECONDS_FOR_ORDERLESS_TXNS,
error::invalid_argument(PROLOGUE_ETRANSACTION_EXPIRATION_TOO_FAR_IN_FUTURE),
);
assert!(nonce_validation::check_and_insert_nonce(sender, nonce, txn_expiration_time), error::invalid_argument(PROLOGUE_ENONCE_ALREADY_USED));
}
Function script_prologue
fun script_prologue(sender: signer, txn_sequence_number: u64, txn_public_key: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, _script_hash: vector<u8>)
Implementation
fun script_prologue(
sender: signer,
txn_sequence_number: u64,
txn_public_key: vector<u8>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
_script_hash: vector<u8>,
) {
// prologue_common with is_simulation set to false behaves identically to the original script_prologue function.
prologue_common(
&sender,
&sender,
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
false,
option::none(),
)
}
Function script_prologue_extended
fun script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_public_key: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, _script_hash: vector<u8>, is_simulation: bool)
Implementation
fun script_prologue_extended(
sender: signer,
txn_sequence_number: u64,
txn_public_key: vector<u8>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
_script_hash: vector<u8>,
is_simulation: bool,
) {
prologue_common(
&sender,
&sender,
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
option::none(),
)
}
Function multi_agent_script_prologue
fun multi_agent_script_prologue(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8)
Implementation
fun multi_agent_script_prologue(
sender: signer,
txn_sequence_number: u64,
txn_sender_public_key: vector<u8>,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<vector<u8>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
) {
// prologue_common and multi_agent_common_prologue with is_simulation set to false behaves identically to the
// original multi_agent_script_prologue function.
prologue_common(
&sender,
&sender,
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_sender_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
false,
option::none(),
);
multi_agent_common_prologue(
secondary_signer_addresses,
secondary_signer_public_key_hashes.map(|x| option::some(x)),
false
);
}
Function multi_agent_script_prologue_extended
fun multi_agent_script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun multi_agent_script_prologue_extended(
sender: signer,
txn_sequence_number: u64,
txn_sender_public_key: vector<u8>,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<vector<u8>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
prologue_common(
&sender,
&sender,
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_sender_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
option::none(),
);
multi_agent_common_prologue(
secondary_signer_addresses,
secondary_signer_public_key_hashes.map(|x| option::some(x)),
is_simulation
);
}
Function multi_agent_common_prologue
fun multi_agent_common_prologue(secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, is_simulation: bool)
Implementation
fun multi_agent_common_prologue(
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
is_simulation: bool,
) {
let num_secondary_signers = secondary_signer_addresses.length();
assert!(
secondary_signer_public_key_hashes.length() == num_secondary_signers,
error::invalid_argument(PROLOGUE_ESECONDARY_KEYS_ADDRESSES_COUNT_MISMATCH),
);
let i = 0;
while ({
// spec {
// invariant i <= num_secondary_signers;
// invariant forall j in 0..i:
// account::exists_at(secondary_signer_addresses[j]);
// invariant forall j in 0..i:
// secondary_signer_public_key_hashes[j] == account::get_authentication_key(secondary_signer_addresses[j]) ||
// (features::spec_simulation_enhancement_enabled() && is_simulation && vector::is_empty(secondary_signer_public_key_hashes[j]));
// account::account_resource_exists_at(secondary_signer_addresses[j])
// && secondary_signer_public_key_hashes[j]
// == account::get_authentication_key(secondary_signer_addresses[j])
// || features::account_abstraction_enabled() && account_abstraction::using_native_authenticator(
// secondary_signer_addresses[j]
// ) && option::spec_some(secondary_signer_public_key_hashes[j]) == account_abstraction::native_authenticator(
// account::exists_at(secondary_signer_addresses[j])
// && secondary_signer_public_key_hashes[j]
// == account::spec_get_authentication_key(secondary_signer_addresses[j])
// || features::spec_account_abstraction_enabled() && account_abstraction::using_native_authenticator(
// secondary_signer_addresses[j]
// ) && option::spec_some(
// secondary_signer_public_key_hashes[j]
// ) == account_abstraction::spec_native_authenticator(
// secondary_signer_addresses[j]
// );
// };
(i < num_secondary_signers)
}) {
let secondary_address = secondary_signer_addresses[i];
assert!(account::exists_at(secondary_address), error::invalid_argument(PROLOGUE_EACCOUNT_DOES_NOT_EXIST));
let signer_public_key_hash = secondary_signer_public_key_hashes[i];
if (!skip_auth_key_check(is_simulation, &signer_public_key_hash)) {
if (signer_public_key_hash.is_some()) {
assert!(
signer_public_key_hash == option::some(account::get_authentication_key(secondary_address)),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
);
} else {
assert!(
allow_missing_txn_authentication_key(secondary_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
)
};
};
i += 1;
}
}
Function fee_payer_script_prologue
fun fee_payer_script_prologue(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, fee_payer_address: address, fee_payer_public_key_hash: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8)
Implementation
fun fee_payer_script_prologue(
sender: signer,
txn_sequence_number: u64,
txn_sender_public_key: vector<u8>,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<vector<u8>>,
fee_payer_address: address,
fee_payer_public_key_hash: vector<u8>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
) {
// prologue_common and multi_agent_common_prologue with is_simulation set to false behaves identically to the
// original fee_payer_script_prologue function.
prologue_common(
&sender,
&create_signer::create_signer(fee_payer_address),
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_sender_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
false,
option::none(),
);
multi_agent_common_prologue(
secondary_signer_addresses,
secondary_signer_public_key_hashes.map(|x| option::some(x)),
false
);
assert!(
fee_payer_public_key_hash == account::get_authentication_key(fee_payer_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
);
}
Function fee_payer_script_prologue_extended
fun fee_payer_script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, fee_payer_address: address, fee_payer_public_key_hash: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun fee_payer_script_prologue_extended(
sender: signer,
txn_sequence_number: u64,
txn_sender_public_key: vector<u8>,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<vector<u8>>,
fee_payer_address: address,
fee_payer_public_key_hash: vector<u8>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
prologue_common(
&sender,
&create_signer::create_signer(fee_payer_address),
ReplayProtector::SequenceNumber(txn_sequence_number),
option::some(txn_sender_public_key),
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
option::none(),
);
multi_agent_common_prologue(
secondary_signer_addresses,
secondary_signer_public_key_hashes.map(|x| option::some(x)),
is_simulation
);
if (!skip_auth_key_check(is_simulation, &option::some(fee_payer_public_key_hash))) {
assert!(
fee_payer_public_key_hash == account::get_authentication_key(fee_payer_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
)
}
}
Function epilogue
Epilogue function is run after a transaction is successfully executed. Called by the Adapter
fun epilogue(account: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
Implementation
fun epilogue(
account: signer,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64,
) {
let addr = signer::address_of(&account);
epilogue_gas_payer(
account,
addr,
storage_fee_refunded,
txn_gas_price,
txn_max_gas_units,
gas_units_remaining
);
}
Function epilogue_extended
fun epilogue_extended(account: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
Implementation
fun epilogue_extended(
account: signer,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64,
is_simulation: bool,
) {
let addr = signer::address_of(&account);
epilogue_gas_payer_extended(
account,
addr,
storage_fee_refunded,
txn_gas_price,
txn_max_gas_units,
gas_units_remaining,
is_simulation
);
}
Function epilogue_gas_payer
Epilogue function with explicit gas payer specified, is run after a transaction is successfully executed. Called by the Adapter
fun epilogue_gas_payer(account: signer, gas_payer: address, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
Implementation
fun epilogue_gas_payer(
account: signer,
gas_payer: address,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64
) {
// epilogue_gas_payer_extended with is_simulation set to false behaves identically to the original
// epilogue_gas_payer function.
epilogue_gas_payer_extended(
account,
gas_payer,
storage_fee_refunded,
txn_gas_price,
txn_max_gas_units,
gas_units_remaining,
false,
);
}
Function epilogue_gas_payer_extended
fun epilogue_gas_payer_extended(account: signer, gas_payer: address, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
Implementation
fun epilogue_gas_payer_extended(
account: signer,
gas_payer: address,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64,
is_simulation: bool,
) {
assert!(txn_max_gas_units >= gas_units_remaining, error::invalid_argument(EOUT_OF_GAS));
let gas_used = txn_max_gas_units - gas_units_remaining;
assert!(
(txn_gas_price as u128) * (gas_used as u128) <= MAX_U64,
error::out_of_range(EOUT_OF_GAS)
);
let transaction_fee_amount = txn_gas_price * gas_used;
// it's important to maintain the error code consistent with vm
// to do failed transaction cleanup.
if (!skip_gas_payment(is_simulation, gas_payer)) {
assert!(
aptos_account::is_fungible_balance_at_least(gas_payer, transaction_fee_amount),
error::out_of_range(PROLOGUE_ECANT_PAY_GAS_DEPOSIT),
);
if (transaction_fee_amount > storage_fee_refunded) {
let burn_amount = transaction_fee_amount - storage_fee_refunded;
transaction_fee::burn_fee(gas_payer, burn_amount);
} else if (transaction_fee_amount < storage_fee_refunded) {
let mint_amount = storage_fee_refunded - transaction_fee_amount;
transaction_fee::mint_and_refund(gas_payer, mint_amount);
};
};
// Increment sequence number
let addr = signer::address_of(&account);
account::increment_sequence_number(addr);
}
Function skip_auth_key_check
fun skip_auth_key_check(is_simulation: bool, auth_key: &option::Option<vector<u8>>): bool
Implementation
inline fun skip_auth_key_check(is_simulation: bool, auth_key: &Option<vector<u8>>): bool {
is_simulation && (auth_key.is_none() || auth_key.borrow().is_empty())
}
Function skip_gas_payment
fun skip_gas_payment(is_simulation: bool, gas_payer: address): bool
Implementation
inline fun skip_gas_payment(is_simulation: bool, gas_payer: address): bool {
is_simulation && gas_payer == @0x0
}
Function unified_prologue
new set of functions
fun unified_prologue(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, txn_sequence_number: u64, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun unified_prologue(
sender: signer,
// None means no need to check, i.e. either AA (where it is already checked) or simulation
txn_sender_public_key: Option<vector<u8>>,
txn_sequence_number: u64,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
unified_prologue_v2(
sender,
txn_sender_public_key,
ReplayProtector::SequenceNumber(txn_sequence_number),
secondary_signer_addresses,
secondary_signer_public_key_hashes,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
)
}
Function unified_prologue_fee_payer
If there is no fee_payer, fee_payer = sender
fun unified_prologue_fee_payer(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, txn_sequence_number: u64, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun unified_prologue_fee_payer(
sender: signer,
fee_payer: signer,
// None means no need to check, i.e. either AA (where it is already checked) or simulation
txn_sender_public_key: Option<vector<u8>>,
// None means no need to check, i.e. either AA (where it is already checked) or simulation
fee_payer_public_key_hash: Option<vector<u8>>,
txn_sequence_number: u64,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
unified_prologue_fee_payer_v2(
sender,
fee_payer,
txn_sender_public_key,
fee_payer_public_key_hash,
ReplayProtector::SequenceNumber(txn_sequence_number),
secondary_signer_addresses,
secondary_signer_public_key_hashes,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
)
}
Function unified_epilogue
fun unified_epilogue(account: signer, gas_payer: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
Implementation
fun unified_epilogue(
account: signer,
gas_payer: signer,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64,
is_simulation: bool,
) {
unified_epilogue_v2(
account,
gas_payer,
storage_fee_refunded,
txn_gas_price,
txn_max_gas_units,
gas_units_remaining,
is_simulation,
false,
)
}
Function unified_prologue_v2
new set of functions to support txn payload v2 format and orderless transactions
fun unified_prologue_v2(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun unified_prologue_v2(
sender: signer,
txn_sender_public_key: Option<vector<u8>>,
replay_protector: ReplayProtector,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
prologue_common(
&sender,
&sender,
replay_protector,
txn_sender_public_key,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
option::none(),
);
multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation);
}
Function unified_prologue_fee_payer_v2
If there is no fee_payer, fee_payer = sender
fun unified_prologue_fee_payer_v2(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Implementation
fun unified_prologue_fee_payer_v2(
sender: signer,
fee_payer: signer,
txn_sender_public_key: Option<vector<u8>>,
fee_payer_public_key_hash: Option<vector<u8>>,
replay_protector: ReplayProtector,
secondary_signer_addresses: vector<address>,
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>,
txn_gas_price: u64,
txn_max_gas_units: u64,
txn_expiration_time: u64,
chain_id: u8,
is_simulation: bool,
) {
prologue_common(
&sender,
&fee_payer,
replay_protector,
txn_sender_public_key,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
option::none(),
);
multi_agent_common_prologue(secondary_signer_addresses, secondary_signer_public_key_hashes, is_simulation);
if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) {
let fee_payer_address = signer::address_of(&fee_payer);
if (fee_payer_public_key_hash.is_some()) {
assert!(
fee_payer_public_key_hash == option::some(account::get_authentication_key(fee_payer_address)),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
);
} else {
assert!(
allow_missing_txn_authentication_key(fee_payer_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
)
};
}
}
Function unified_epilogue_v2
fun unified_epilogue_v2(account: signer, gas_payer: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool, is_orderless_txn: bool)
Implementation
fun unified_epilogue_v2(
account: signer,
gas_payer: signer,
storage_fee_refunded: u64,
txn_gas_price: u64,
txn_max_gas_units: u64,
gas_units_remaining: u64,
is_simulation: bool,
is_orderless_txn: bool,
) {
assert!(txn_max_gas_units >= gas_units_remaining, error::invalid_argument(EOUT_OF_GAS));
let gas_used = txn_max_gas_units - gas_units_remaining;
assert!(
(txn_gas_price as u128) * (gas_used as u128) <= MAX_U64,
error::out_of_range(EOUT_OF_GAS)
);
let transaction_fee_amount = txn_gas_price * gas_used;
let gas_payer_address = signer::address_of(&gas_payer);
// it's important to maintain the error code consistent with vm
// to do failed transaction cleanup.
if (!skip_gas_payment(
is_simulation,
gas_payer_address
)) {
assert!(
aptos_account::is_fungible_balance_at_least(gas_payer_address, transaction_fee_amount),
error::out_of_range(PROLOGUE_ECANT_PAY_GAS_DEPOSIT),
);
if (transaction_fee_amount > storage_fee_refunded) {
let burn_amount = transaction_fee_amount - storage_fee_refunded;
transaction_fee::burn_fee(gas_payer_address, burn_amount);
permissioned_signer::check_permission_consume(
&gas_payer,
(burn_amount as u256),
GasPermission {}
);
} else if (transaction_fee_amount < storage_fee_refunded) {
let mint_amount = storage_fee_refunded - transaction_fee_amount;
transaction_fee::mint_and_refund(gas_payer_address, mint_amount);
permissioned_signer::increase_limit(
&gas_payer,
(mint_amount as u256),
GasPermission {}
);
};
};
if (!is_orderless_txn) {
// Increment sequence number
let addr = signer::address_of(&account);
account::increment_sequence_number(addr);
}
}
Function versioned_prologue
fun versioned_prologue(sender: signer, fee_payer: signer, args: transaction_validation::PrologueArgs)
Implementation
fun versioned_prologue(sender: signer, fee_payer: signer, args: PrologueArgs) {
match (args) {
V1 {
txn_sender_public_key,
fee_payer_public_key_hash,
replay_protector,
secondary_signer_addresses,
secondary_signer_public_key_hashes,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
txn_limits_request,
} => {
prologue_common(
&sender,
&fee_payer,
replay_protector,
txn_sender_public_key,
txn_gas_price,
txn_max_gas_units,
txn_expiration_time,
chain_id,
is_simulation,
txn_limits_request,
);
multi_agent_common_prologue(
secondary_signer_addresses,
secondary_signer_public_key_hashes,
is_simulation,
);
let fee_payer_address = signer::address_of(&fee_payer);
if (fee_payer_address != signer::address_of(&sender)) {
if (!skip_auth_key_check(is_simulation, &fee_payer_public_key_hash)) {
if (fee_payer_public_key_hash.is_some()) {
let fee_payer_public_key_hash = fee_payer_public_key_hash.destroy_some();
assert!(
fee_payer_public_key_hash == account::get_authentication_key(fee_payer_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
);
} else {
assert!(
allow_missing_txn_authentication_key(fee_payer_address),
error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY),
);
};
};
};
},
}
}
Function versioned_epilogue
fun versioned_epilogue(account: signer, fee_payer: signer, args: transaction_validation::EpilogueArgs)
Implementation
fun versioned_epilogue(account: signer, fee_payer: signer, args: EpilogueArgs) {
match (args) {
V1 {
fee_statement,
txn_gas_price,
txn_max_gas_units,
gas_units_remaining,
is_simulation,
is_orderless_txn,
} => {
unified_epilogue_v2(
account,
fee_payer,
fee_statement.storage_fee_refund_octas(),
txn_gas_price,
txn_max_gas_units,
gas_units_remaining,
is_simulation,
is_orderless_txn,
);
transaction_fee::emit_fee_statement(fee_statement);
},
}
}
Specification
High-level Requirements
| No. | Requirement | Criticality | Implementation | Enforcement |
|---|---|---|---|---|
| 1 | The sender of a transaction should have sufficient coin balance to pay the transaction fee. | High | The prologue_common function asserts that the transaction sender has enough coin balance to be paid as the max_transaction_fee. | Formally verified via PrologueCommonAbortsIf. Moreover, the native transaction validation patterns have been manually audited. |
| 2 | All secondary signer addresses are verified to be authentic through a validation process. | Critical | The function multi_agent_script_prologue ensures that each secondary signer address undergoes authentication validation, including verification of account existence and authentication key matching, confirming their authenticity. | Formally verified via multi_agent_script_prologue. Moreover, the native transaction validation patterns have been manually audited. |
| 3 | After successful execution, base the transaction fee on the configuration set by the features library. | High | The epilogue function collects the transaction fee for either redistribution or burning based on the feature::collect_and_distribute_gas_fees result. | Formally Verified via epilogue. Moreover, the native transaction validation patterns have been manually audited. |
Module-level Specification
pragma verify = true;
pragma aborts_if_is_strict;
Function grant_gas_permission
public fun grant_gas_permission(master: &signer, permissioned: &signer, gas_amount: u64)
pragma aborts_if_is_partial;
Function revoke_gas_permission
public fun revoke_gas_permission(permissioned: &signer)
pragma aborts_if_is_partial;
Function initialize
public(friend) fun initialize(aptos_framework: &signer, script_prologue_name: vector<u8>, module_prologue_name: vector<u8>, multi_agent_prologue_name: vector<u8>, user_epilogue_name: vector<u8>)
Ensure caller is aptos_framework.
Aborts if TransactionValidation already exists.
let addr = signer::address_of(aptos_framework);
aborts_if !system_addresses::is_aptos_framework_address(addr);
aborts_if exists<TransactionValidation>(addr);
ensures exists<TransactionValidation>(addr);
Create a schema to reuse some code. Give some constraints that may abort according to the conditions.
schema PrologueCommonAbortsIf {
sender: &signer;
gas_payer: &signer;
replay_protector: ReplayProtector;
txn_authentication_key: Option<vector<u8>>;
txn_gas_price: u64;
txn_max_gas_units: u64;
txn_expiration_time: u64;
chain_id: u8;
aborts_if !exists<CurrentTimeMicroseconds>(@aptos_framework);
aborts_if !(timestamp::now_seconds() < txn_expiration_time);
aborts_if !exists<ChainId>(@aptos_framework);
aborts_if !(chain_id::get() == chain_id);
let transaction_sender = signer::address_of(sender);
let gas_payer_addr = signer::address_of(gas_payer);
}
Function prologue_common
fun prologue_common(sender: &signer, gas_payer: &signer, replay_protector: transaction_validation::ReplayProtector, txn_authentication_key: option::Option<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool, txn_limits_request: option::Option<transaction_limits::UserTxnLimitsRequest>)
pragma verify = false;
include PrologueCommonAbortsIf;
Function check_for_replay_protection_regular_txn
fun check_for_replay_protection_regular_txn(sender_address: address, gas_payer_address: address, txn_sequence_number: u64)
pragma verify = false;
Function check_for_replay_protection_orderless_txn
fun check_for_replay_protection_orderless_txn(sender: address, nonce: u64, txn_expiration_time: u64)
pragma verify = false;
Function script_prologue
fun script_prologue(sender: signer, txn_sequence_number: u64, txn_public_key: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, _script_hash: vector<u8>)
pragma verify = false;
schema MultiAgentPrologueCommonAbortsIf {
secondary_signer_addresses: vector<address>;
secondary_signer_public_key_hashes: vector<Option<vector<u8>>>;
is_simulation: bool;
let num_secondary_signers = len(secondary_signer_addresses);
aborts_if len(secondary_signer_public_key_hashes) != num_secondary_signers;
// This enforces high-level requirement 2:
aborts_if exists i in 0..num_secondary_signers:
!account::spec_exists_at(secondary_signer_addresses[i]);
aborts_if exists i in 0..num_secondary_signers:
!can_skip(features::spec_simulation_enhancement_enabled(), is_simulation, secondary_signer_public_key_hashes[i]) &&
option::is_some(secondary_signer_public_key_hashes[i]) && option::borrow(
secondary_signer_public_key_hashes[i]
) !=
account::spec_get_authentication_key(secondary_signer_addresses[i]);
ensures forall i in 0..num_secondary_signers:
account::spec_exists_at(secondary_signer_addresses[i]);
ensures forall i in 0..num_secondary_signers:
option::is_none(secondary_signer_public_key_hashes[i]) || option::borrow(
secondary_signer_public_key_hashes[i]
) ==
account::spec_get_authentication_key(secondary_signer_addresses[i])
|| can_skip(features::spec_simulation_enhancement_enabled(), is_simulation, secondary_signer_public_key_hashes[i]);
}
fun can_skip(feature_flag: bool, is_simulation: bool, auth_key: Option<vector<u8>>): bool {
features::spec_simulation_enhancement_enabled() && is_simulation && option::is_none(auth_key)
}
Function script_prologue_extended
fun script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_public_key: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, _script_hash: vector<u8>, is_simulation: bool)
pragma verify = false;
include PrologueCommonAbortsIf {
gas_payer: sender,
txn_authentication_key: option::spec_some(txn_public_key),
replay_protector: ReplayProtector::SequenceNumber(txn_sequence_number),
};
Function multi_agent_script_prologue
fun multi_agent_script_prologue(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8)
pragma verify = false;
Function multi_agent_script_prologue_extended
fun multi_agent_script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
Aborts if length of public key hashed vector not equal the number of singers.
pragma verify_duration_estimate = 120;
let gas_payer = sender;
pragma verify = false;
Function multi_agent_common_prologue
fun multi_agent_common_prologue(secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, is_simulation: bool)
pragma aborts_if_is_partial;
Function fee_payer_script_prologue
fun fee_payer_script_prologue(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, fee_payer_address: address, fee_payer_public_key_hash: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8)
pragma verify = false;
Function fee_payer_script_prologue_extended
fun fee_payer_script_prologue_extended(sender: signer, txn_sequence_number: u64, txn_sender_public_key: vector<u8>, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<vector<u8>>, fee_payer_address: address, fee_payer_public_key_hash: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
pragma aborts_if_is_partial;
pragma verify_duration_estimate = 120;
aborts_if !features::spec_is_enabled(features::FEE_PAYER_ENABLED);
let gas_payer = create_signer::spec_create_signer(fee_payer_address);
include PrologueCommonAbortsIf {
gas_payer,
replay_protector: ReplayProtector::SequenceNumber(txn_sequence_number),
txn_authentication_key: option::spec_some(txn_sender_public_key),
};
aborts_if !account::spec_exists_at(fee_payer_address);
aborts_if !(fee_payer_public_key_hash == account::spec_get_authentication_key(fee_payer_address));
aborts_if !features::spec_fee_payer_enabled();
Function epilogue
fun epilogue(account: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
pragma verify = false;
Function epilogue_extended
fun epilogue_extended(account: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
Abort according to the conditions.
AptosCoinCapabilities and CoinInfo should exists.
Skip transaction_fee::burn_fee verification.
pragma verify = false;
include EpilogueGasPayerAbortsIf { gas_payer: signer::address_of(account) };
Function epilogue_gas_payer
fun epilogue_gas_payer(account: signer, gas_payer: address, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
pragma verify = false;
Function epilogue_gas_payer_extended
fun epilogue_gas_payer_extended(account: signer, gas_payer: address, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
Abort according to the conditions.
AptosCoinCapabilities and CoinInfo should exist.
Skip transaction_fee::burn_fee verification.
pragma verify = false;
include EpilogueGasPayerAbortsIf;
Function unified_prologue
fun unified_prologue(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, txn_sequence_number: u64, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
pragma verify = false;
Function unified_prologue_fee_payer
fun unified_prologue_fee_payer(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, txn_sequence_number: u64, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
pragma verify = false;
Function unified_epilogue
fun unified_epilogue(account: signer, gas_payer: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool)
pragma verify = false;
Function unified_prologue_v2
fun unified_prologue_v2(sender: signer, txn_sender_public_key: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
pragma verify = false;
Function unified_prologue_fee_payer_v2
fun unified_prologue_fee_payer_v2(sender: signer, fee_payer: signer, txn_sender_public_key: option::Option<vector<u8>>, fee_payer_public_key_hash: option::Option<vector<u8>>, replay_protector: transaction_validation::ReplayProtector, secondary_signer_addresses: vector<address>, secondary_signer_public_key_hashes: vector<option::Option<vector<u8>>>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, is_simulation: bool)
pragma verify = false;
Function unified_epilogue_v2
fun unified_epilogue_v2(account: signer, gas_payer: signer, storage_fee_refunded: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, is_simulation: bool, is_orderless_txn: bool)
pragma verify = false;
Function versioned_prologue
fun versioned_prologue(sender: signer, fee_payer: signer, args: transaction_validation::PrologueArgs)
pragma verify = false;
Function versioned_epilogue
fun versioned_epilogue(account: signer, fee_payer: signer, args: transaction_validation::EpilogueArgs)
pragma verify = false;
schema EpilogueGasPayerAbortsIf {
account: signer;
gas_payer: address;
storage_fee_refunded: u64;
txn_gas_price: u64;
txn_max_gas_units: u64;
gas_units_remaining: u64;
aborts_if !(txn_max_gas_units >= gas_units_remaining);
let gas_used = txn_max_gas_units - gas_units_remaining;
aborts_if !(txn_gas_price * gas_used <= MAX_U64);
let transaction_fee_amount = txn_gas_price * gas_used;
let addr = signer::address_of(account);
let pre_account = global<account::Account>(addr);
let post account = global<account::Account>(addr);
aborts_if !exists<CoinStore<AptosCoin>>(gas_payer);
aborts_if !exists<Account>(addr);
aborts_if !(global<Account>(addr).sequence_number < MAX_U64);
ensures account.sequence_number == pre_account.sequence_number + 1;
let amount_to_burn = transaction_fee_amount - storage_fee_refunded;
let apt_addr = type_info::type_of<AptosCoin>().account_address;
let maybe_apt_supply = global<CoinInfo<AptosCoin>>(apt_addr).supply;
let total_supply_enabled = option::is_some(maybe_apt_supply);
let apt_supply = option::borrow(maybe_apt_supply);
let apt_supply_value = optional_aggregator::optional_aggregator_value(apt_supply);
let post post_maybe_apt_supply = global<CoinInfo<AptosCoin>>(apt_addr).supply;
let post post_apt_supply = option::borrow(post_maybe_apt_supply);
let post post_apt_supply_value = optional_aggregator::optional_aggregator_value(post_apt_supply);
aborts_if amount_to_burn > 0 && !exists<AptosCoinCapabilities>(@aptos_framework);
aborts_if amount_to_burn > 0 && !exists<CoinInfo<AptosCoin>>(apt_addr);
aborts_if amount_to_burn > 0 && total_supply_enabled && apt_supply_value < amount_to_burn;
ensures total_supply_enabled ==> apt_supply_value - amount_to_burn == post_apt_supply_value;
let amount_to_mint = storage_fee_refunded - transaction_fee_amount;
let total_supply = coin::supply<AptosCoin>;
let post post_total_supply = coin::supply<AptosCoin>;
aborts_if amount_to_mint > 0 && !exists<CoinStore<AptosCoin>>(addr);
aborts_if amount_to_mint > 0 && !exists<AptosCoinMintCapability>(@aptos_framework);
aborts_if amount_to_mint > 0 && total_supply + amount_to_mint > MAX_U128;
ensures amount_to_mint > 0 ==> post_total_supply == total_supply + amount_to_mint;
let aptos_addr = type_info::type_of<AptosCoin>().account_address;
aborts_if (amount_to_mint != 0) && !exists<coin::CoinInfo<AptosCoin>>(aptos_addr);
include coin::CoinAddAbortsIf<AptosCoin> { amount: amount_to_mint };
}