Scheduled Transactions
AIP-125 - Scheduled Transactions
Section titled “AIP-125 - Scheduled Transactions”Summary
Section titled “Summary”This AIP introduces scheduled transactions, enabling users to specify transactions that will be executed automatically when certain conditions are met, such as a specific time. The flow involves two key transactions: (i) a scheduling transaction, where the user submits and stores the parameters of the future transaction along with its trigger; and (ii) the scheduled transaction itself, which is executed once the trigger condition is satisfied. This mechanism allows for flexible, autonomous execution of user-defined logic without requiring manual resubmission at execution time.
This AIP focuses on time-based transaction triggers (other triggers will be left for future AIPs).
Out of scope
Section titled “Out of scope”Privacy
Section titled “Privacy”Scheduled transactions will be stored on-chain, and thus visible publicly. Providing privacy for them is out of scope. Users should expect transactions to be publicly accessible, and should not store sensitive information.
Additional triggers
Section titled “Additional triggers”Other triggers (e.g., event-based triggers) are not targeted by this AIP. But will be considered in future AIPs. This AIP lays the foundation for the first type of trigger.
Failure Retries
Section titled “Failure Retries”If a scheduled transaction fails during execution, no retry attempts are made. However, we emit a transaction cancellation event with failure code to the user. The user must manually reschedule the transaction.
Future execution gas markets
Section titled “Future execution gas markets”This AIP defers gas market prediction to future enhancements. Rather than providing potentially inaccurate estimates for future gas markets or guarentees of future block space, we empower users to set appropriate gas unit prices based on their transaction’s priority and urgency. Future AIPs will address these.
Deterministic slicing
Section titled “Deterministic slicing”We don’t implement transaction slicing at the blockchain protocol level. Instead, we enable users to break down long-running operations into smaller, manageable segments through an asynchronous execution model that leverages our rescheduling mechanism.
High-level Overview
Section titled “High-level Overview”The scheduling framework provides a mechanism that enables users to schedule transactions for future execution programmatically. Users can submit a scheduling transaction specifying a target function, a future execution time (in milliseconds), the maximum gas they’re willing to spend, and the price per gas unit. To prevent denial-of-service or spam, users must deposit the full gas budget up front, which is held in a dedicated fungible asset store. Users may cancel scheduled transactions at any time before execution to reclaim their deposit.
Once submitted, scheduled transactions are stored in a queue data structure sorted by execution time, with 1ms granularity. At the beginning of each block, the blockchain core system checks whether any scheduled transactions are due. If so, they are considered as candidate transactions and prioritized using specified price per gas unit. Transactions that are not selected for execution are retried in future blocks, up to a fixed expiration window (around 100 blocks).
When a scheduled transaction is executed successfully, the actual gas fee is computed and deducted from the deposit. Since it is impractical for users to predict the optimal gas price in advance, they provide a max_gas_unit_price. The system uses the lesser of this maximum and the highest gas price of any transaction in the block, ensuring fairly prioritized inclusion without excessive overpayment. Any unused portion of the deposit is refunded to the user. The transaction can also schedule a subsequent execution at a future interval to enable async or recurring operations.
This approach leverages existing transaction and gas accounting infrastructure, ensures fair prioritization alongside regular transactions, deters misuse via prepaid gas deposits, and creates a foundation for more advanced onchain flows.
Impact
Section titled “Impact”This is a new feature that provides composability across time. It creates a foundation for more advanced onchain flows like delayed payments, subscriptions, time shifted computations, recurring tasks, async programming patterns, and mission-critical operations that demand sub-millisecond precision — all executed within the deterministic environment of the Aptos blockchain.
Depends on AIP-112 (Function Values in the Move VM), FA migration.
Alternative Solutions
Section titled “Alternative Solutions”Storing scheduled transactions off-chain is one alternative. However, it introduces several drawbacks that make it a less viable alternative to on-chain scheduling. It requires maintaining additional infrastructure, leading to increased operational complexity, higher costs, and potential service fees if run by third parties. Guaranteeing data consistency and correctness across nodes becomes difficult, and there are inherent trust and custody risks in delegating execution authority to external systems. This setup also fragments the developer and user experience, as scheduled transactions may not appear in standard APIs or explorers, complicating tooling and observability.
Specification and Implementation Details
Section titled “Specification and Implementation Details”As mentioned in the overview, the implementation is split across move framework and blockchain core. A unique composite key is generated for every scheduled txn using its schedule time, gas_priority and ‘txn_id’ which is computed using the SHA3-256 hash of the txn.
Move framework implementation
Section titled “Move framework implementation”In the move framework ‘ScheduledTransaction’ is defined as below.
struct ScheduledTransaction has copy, drop, store { /// 32 bytes sender_addr: address, /// UTC timestamp in milliseconds scheduled_time_ms: u64, /// Maximum gas to spend for this transaction max_gas_amount: u64, /// Charged @ lesser of {max_gas_unit_price, max_gas_unit_price other than this in the block executed} max_gas_unit_price: u64, /// Option to pass a signer to the function pass_signer: bool, /// Variables are captured in the closure; optionally a signer is passed; no return f: ScheduledFunction }
enum ScheduledFunction has copy, store, drop { V1(|Option<signer>| has copy + store + drop), }
/// First sorted in ascending order of time, then on gas priority, and finally on txn_id /// gas_priority = U64_MAX - gas_unit_price; we want higher gas_unit_price to come before lower gas_unit_price /// txn_id = sha3_256(bcs::to_bytes(&txn)) struct ScheduleMapKey has copy, drop, store { /// UTC timestamp in the granularity of ms time: u64, gas_priority: u64, /// SHA3-256 txn_id: vector<u8> }Users can submit or cancel a ScheduledTransaction. These actions are restricted to the user identified by the sender_handle, ensuring that only the fee/deposit paying user has the authority to insert or cancel the transaction.
/// Insert a scheduled transaction into the queue. ScheduleMapKey is returned to user, which can be used to cancel the txn. public fun insert(sender: &signer, txn: ScheduledTransaction): ScheduleMapKey {...}
/// Cancel a scheduled transaction, must be called by the signer who originally scheduled the transaction. public fun cancel(sender: &signer, key: ScheduleMapKey) {...}Scheduled txns are stored in a BigOrderedMap sorted by the compsite key ‘ScheduleMapKey’ whose primary component is schedule time. Hence each block has to only look upto the txns it needs to run.
struct ScheduleQueue has key { /// key_size = 48 bytes; value_size = key_size + object ref size = 80 bytes (48 + 32) schedule_map: BigOrderedMap<ScheduleMapKey, ScheduledTransaction>, }The framework also includes logic to retrieve transactions that are ready for execution. Because the ScheduleQueue is sorted by execution time, only the earliest segment of the queue needs to be scanned to find eligible transactions. The framework establishes ‘GET_READY_TRANSACTIONS_LIMIT’ as the maximum number of scheduled transactions permitted per block. Since transactions are prioritized by gas price, when too many transactions are scheduled for execution at the same time, lower-priority transactions will be deferred to subsequent blocks. Transactions that remain unexecuted for approximately 100 blocks (about 10 seconds) are considered expired and are removed from the ScheduleQueue.
/// We pass around only needed info struct ScheduledTransactionInfoWithKey has drop { sender_addr: address, max_gas_amount: u64, max_gas_unit_price: u64, /// To be determined during execution gas_unit_price_charged: u64, key: ScheduleMapKey }
/// The maximum number of scheduled transactions that can be run in a block const GET_READY_TRANSACTIONS_LIMIT: u64 = 100;
/// Gets txns due to be run; also expire txns that could not be run for a while (mostly due to low gas priority) fun get_ready_transactions(timestamp_ms: u64): vector<ScheduledTransactionInfoWithKey>To store the gas fee deposit collected at the time of scheduling (or re-scheduling), we use a framework reserved account owned fa store.
// Create owner account for handling deposits let owner_addr = @0xb; let (owner_signer, owner_cap) = account::create_framework_reserved_account(owner_addr);
// Initialize fungible store for the owner let metadata = ensure_paired_metadata<AptosCoin>(); primary_fungible_store::ensure_primary_store_exists( signer::address_of(&owner_signer), metadata );
/// Signer for the store for gas fee deposits struct GasFeeDepositStoreSignerCap has key { cap: account::SignerCapability }The actual gas fees are charged, and any excess deposit is refunded in the epilogue of the executed scheduled transaction. To preserve the ability to execute transactions in parallel during block execution via BlockSTM, the scheduled transaction is only marked for removal upon execution. Its actual removal from the ScheduleQueue takes place during the block prologue of the subsequent block.
Additionally, two events are introduced for observability and failure tracking.
enum CancelledTxnCode has drop, store { /// Scheduling service is stopped Shutdown, /// Transaction was expired Expired, /// Transcation failed to execute Failed, }
#[event] struct TransactionFailedEvent has drop, store { key: ScheduleMapKey, sender_addr: address, cancelled_txn_code: CancelledTxnCode, }
#[event] struct ShutdownEvent has drop, store { complete: bool, }TransactionFailedEvent is emitted when a scheduled transaction either fails during execution or cannot be executed due to conditions such as expiry or system shutdown. Separately, ShutdownEvent is emitted when the scheduled transaction subsystem undergoes a controlled shutdown. The complete field in ShutdownEvent indicates whether the shutdown was cleanly completed.
Blockchain core implementation
Section titled “Blockchain core implementation”Getting ready transactions
Section titled “Getting ready transactions”Scheduled transactions are retreived during the block pipeline’s ‘execute’ stage. However, it must wait for the parent block to complete execution before fetching ready transactions, as the parent block’s completion determines the starting point for processing the ScheduleQueue.
Removing executed transactions
Section titled “Removing executed transactions”Executed transactions do not remove themselves from the ScheduleQueue to prevent multiple transactions conflicting on the queue and thereby reducing the block throughput. Instead, they are placed in a parallelized removal table, with actual deletion occurring during the execution of next block’s prologue transaction.
Gas prioritization
Section titled “Gas prioritization”The user transactions are shuffled by a use-case aware shuffler (AIP- 68). We then insert scheduled transactions into appropriate positions within this reordered sequence, determined by their gas priority.
Reference Implementation
Section titled “Reference Implementation”PR: aptos-labs/aptos-core/16346
What is the feature flag(s)? If there is no feature flag, how will this be enabled? {WIP}
Testing
Section titled “Testing”- Framework unit tests
- Blockchain core tests
- E2E tests
- Load tests
- Scheduled transactions do not slow down the TPS of regular execution.
- Scheduled Transactions Perf tests
- We must be able to process atleast 100 (perhaps upto 1000) scheduled txns per block without slowing down the block.
Risks and Drawbacks
Section titled “Risks and Drawbacks”Gas Market Volatility
Section titled “Gas Market Volatility”Predicting future gas prices is inherently challenging due to the dynamic nature of blockchain networks. When you schedule a transaction, you must set a gas price now for execution later, but gas markets can fluctuate significantly due to network congestion, sudden activity spikes, or changing user demand. This means your scheduled transaction might execute with a gas price that’s either too low (causing delays or failures) or not get a chance to execute at all, making it difficult to guarantee that your intended gas prioritization will be effective at the actual execution time.
Best Effort Limitations
Section titled “Best Effort Limitations”The scheduling system cannot provide absolute guarantees. Various factors such as network congestion, gas market conditions, block space limitations, or unexpected system load may cause scheduled transactions to be delayed, reordered, or executed under different conditions than originally anticipated. Users with strict timing requirements, guaranteed execution needs, or specific ordering dependencies may need to actively monitor blockchain conditions and adjust their scheduling strategies accordingly.
Privacy
Section titled “Privacy”Scheduled transactions will be stored on the blockchain, making them publicly visible. This means the details of these transactions are accessible to anyone. It’s crucial that sensitive information is not included in these transactions. Furthermore, due to their public nature, scheduled transactions are vulnerable to front-running risks, where other transactions might attempt to exploit pending transactions.
- Can this proposal impact backward compatibility?
- No
- What is the mitigation plan for each risk or drawback?
- We keep an ability to shutdown scheduled txns and refund deposit
- Do we want an ability to temporarily pause the ‘scheduled txns’ feature ???
Security Considerations
Section titled “Security Considerations”
- How can this AIP potentially impact the security of the network and its users? How is this impact mitigated?
- Are there specific parts of the code that could introduce a security issue if not implemented properly?
- Link tests (e.g. unit, end-to-end, property, fuzz) in the reference implementation that validate both expected and unexpected behavior of this proposal
- Include any security-relevant documentation related to this proposal (e.g. protocols or cryptography specifications)
{WIP}
Future Potential
Section titled “Future Potential”We can build the below alongside or by directly extending Scheduled Transactions.
- Event based triggers
- Future execution gas markets
-
- Guaranteed execution
-
- Block Space Auctioning
- Deterministic slicing
Timeline
Section titled “Timeline”Suggested implementation timeline
Section titled “Suggested implementation timeline”Q2 2025