A smart contract never does anything by itself: it runs when a transaction calls it and then stops. Anything that has to happen at a later time, like a funding rate update or the next monthly salary payment, needs someone outside the chain to send a transaction at the right moment. Today, on Monad and many other EVM chains, including Ethereum, that someone is a keeper (colloquially, a crank), an off-chain process run by the app team or by a third party, that watches the clock and submits transactions on behalf of the app or its users.
Keepers work, but they are a dependency that users do not see. A perp DEX that updates funding every few minutes depends on its keeper being online, funded and not censored. Recurring user actions on most chains are driven by an off-chain executor that users have to trust with timing and sometimes with custody. The contracts are on-chain, but the application works only while the infrastructure around them keeps running. A keeper does not have to be malicious to fail. An outage, an empty gas wallet or a bug in the off-chain infrastructure is enough for the on-chain application to no longer function.
We propose a protocol-level alternative for Monad: scheduled transactions. Any account, whether a user’s wallet or a contract in the middle of a call, can schedule a transaction for a future block. Once the request is accepted, the protocol guarantees that the transaction will run in that block (unless canceled). Both the scheduled transaction and, for contracts, the code deciding whether it can be canceled live on-chain, so anyone can check ahead of time that the call is scheduled and under what conditions it can still be withdrawn. This removes the dependence on keepers and allows apps to be brought fully on-chain.

How It Works, in Short
- Reserved block space. A fixed share of every block is reserved for scheduled transactions. The reserved capacity of a given block opens up gradually as the block gets closer.
- Scheduling. Any account, including smart contracts, can call
schedule(target_block, to, value, gas_limit, data)on the scheduling precompile. If the target block’s scheduled lane hYas room, the request is accepted and recorded in state, where it stays visible until it runs. The caller then becomes the owner of the scheduled transaction and is the only one who can cancel it (if the owner is a smart contract, whether the transaction can be canceled depends on the contract’s design: a contract that offers no way to cancel has made an irrevocable commitment). - Prepayment. The gas for the future call is paid at scheduling time. The price is based on the base fee at that moment and rises as the target block’s scheduled capacity fills up. Any MON the call will transfer is put in escrow, so a scheduled transaction cannot fail for lack of MON.
- Execution. The protocol appends the scheduled transactions due in each block at its end, where they appear like any other transaction, with receipts and logs. Each scheduled transaction links back to the transaction that scheduled it to facilitate debugging and auditing.
- Contracts scheduling themselves. Because contracts can call the
schedule()function, one can build on-chain cron jobs: a contract can schedule its next invocation as the very last thing it does in a call, paying for it from its own balance. The scheduling logic lives in the contract’s code for anyone to audit. Each transaction is prepaid individually, but the contract can commit to funding several future transactions, e.g., by setting aside a portion of its MON balance that it can only spend on scheduling.
What You Can Build
Scheduled transactions unlock far more than a replacement for keepers. They let users and applications alike set up interactions that recur on a cadence, happen once at a chosen moment, or follow from something that happened earlier.
- Cadenced. For app teams: on-chain keepers of every kind, from funding rate updates and payments on a perp DEX to vault rebalancing and safety checks, and game loops that advance on their own. For users: recurring payments and vesting payouts that arrive on their own, and staking rewards that compound automatically.
- One-shot. Auctions and lotteries that settle at a fixed block without a crank. DAO votes whose outcome executes when the voting period ends. Options that settle themselves at expiry. Unstaking followed by delayed withdrawal (
WITHDRAWAL_DELAYepochs). Expiring token approvals: the user sets a deadline and the approval revokes itself. - Reactive. Escrowed funds that return to a user if the counterparty goes silent. Orders that get cleaned up after expiration. Expensive calculations that pause and resume in a later block (this enables asynchronous programming practices).
The rest of this post works through five of these examples: on-chain keepers, recurring payments, long-running (asynchronous) computations, automatic refunds in atomic swaps and order-book cleanup.
Are you a builder? The technical specification is up for discussion on the Monad forum: Scheduled Transactions. If part of the design does not fit how your application works, we would like to hear your input.
On-Chain Keepers
An app team has a smart contract C and wants C.f() to be called periodically as a cron job. Examples include funding rate updates, auto-rebalancing for vaults and essentially any app whose contracts require “periodic poking”.

Without scheduled transactions: an off-chain daemon periodically submits transactions calling C.f(). This is a single point of failure, increases app onboarding and maintenance costs, and prevents apps from becoming truly decentralized.
With scheduled transactions: the app team sets up f() such that the last action taken by f() is schedule(n + 100, C.f()): every time f() runs in a block n, it schedules itself to run again in 100 blocks. At each rescheduling, C pays for the gas of the next invocation, without having to commit more than one call’s worth of gas at a time (pay-as-you-go). Whether f() reschedules at all is itself contract logic (see below).
- Highly Configurable and Transparent. The exact scheduling logic is configurable by the app developers: skipping a booking when a run is already pending or the job has been paused, conditionally enabling or disabling scheduling, setting the retry logic for when scheduling fails due to insufficient block capacity, or booking several runs in advance rather than one at a time. The contract code logic can be verified and audited, and all pending runs are visible on-chain.
- Censorship-Resistant. Off-chain daemons submit periodic transactions, which can get censored. In contrast, on-chain cron jobs end only if
Ccannot pay for its next run or its own logic stops it. Even if the scheduling budget of a target block is exhausted, a later block will have available capacity unless demand is unusually high, often within the next couple of blocks and easily accessible with the correct retry logic. - Simpler Funding Model. Gas costs are paid out of
C’s account instead of the daemon’s account, and the former should be generally more trustworthy. This also unlocks seamless self-funding: the contract can fund this gas from commissions.
Recurring Payments
Say Alice wants to pay Bob a salary of 5,000 USDC every month. The same considerations apply in many other contexts, like vesting payouts.

Without scheduled transactions, Alice has several options:
- Alice signs and submits one transaction every month, for as long as Bob works for her. [Bad UX for Alice]
- Alice deposits a year’s worth of salary (60,000 USDC) into a streaming contract that Bob calls periodically to claim his salary. Alice commits a large sum upfront rather than being able to fund each leg from ongoing income. To continue paying Bob after 1 year, Alice has to intervene. [Capital locked for Alice & Bad UX for both]
- Alice sets up a smart account (an EIP-7702 delegated EOA) whose logic guards against paying Bob more often than agreed. She keeps custody of her funds, but her wallet cannot call itself: a keeper must trigger each payment, and keepers charge a commission. [Keeper-dependent & Costly]
With scheduled transactions: Alice sets herself up as a smart account (delegated EOA), delegating to a contract implementing a pay_salary() method. The pay_salary() method transfers 5,000 USDC to Bob and, as its last action, schedules pay_salary() on Alice’s own account to run again in ONE_MONTH_IN_BLOCKS ≈ 8_640_000 blocks (n.b., computed at today’s 300ms blocks on 30-day months). If the protocol caps how far ahead one can schedule at less than a month, the contract can bridge the gap with cheap intermediate hops, each rescheduling itself closer to the payment date. Custom logic can be added to stop the payments, or to change the amount. With this approach, Alice:
- Signs and submits a single transaction to bootstrap the process;
- Keeps custody of her funds at all times;
- Avoids pre-funding several months of salary;
- Relies on no third parties;
- Pays no additional commissions.
Long-Running Tasks (Asynchronous Computation)
Currently, there is no way for a single computation to span multiple blocks. One can get a decent approximation without scheduled transactions: the first transaction does as much work as a transaction’s worth of gas permits and then saves the intermediate state in storage. A follow-up transaction continues from where the previous one left off, and so on. This requires external submission of each follow-up transaction.

Scheduled transactions allow automating this: the first transaction saves the required state and schedules the continuation. The user submits a single transaction, with the rest happening automatically.
Future work may provide language/compiler-level tooling making the “save and restore” process easier to program, e.g., by providing async-await-style syntax.
Automatic Refunds in Atomic Swaps
Imagine Alice and Bob want to perform a swap. To do this, they deposit their respective funds into an escrow contract C. After both deposits have happened, C executes the swap. There is also a deadline for the two parties to act, after which any outstanding input funds should be returned to the owning parties. This last part is problematic: if only Alice supplies her part and Bob goes silent, she will have to manually send a second transaction to recover her funds. Even worse, this transaction can be censored, preventing Alice from recovering the funds, so the swap isn’t truly “atomic” in a strict sense.

These shortcomings are addressed with scheduled transactions: there are several ways to realize the details here, e.g., when Alice puts her part in, C schedules a transaction due at or a little after the target deadline. If the swap hasn’t completed in time, the scheduled transaction fires and Alice gets her funds back unconditionally. If the swap does complete in time, C cancels this transaction (or the transaction just executes as a no-op). This ensures the swap is truly atomic without requiring separate intervention to recover locked funds.
Order-Book Cleanup
Imagine a Central Limit Order Book (CLOB) that wants to implement support for Good-Till-Date (GTD) orders, i.e., orders that are only valid until a certain target time. Alice would send her order in block n1 with expiration at block n2. Orders that are past their expiration block remain in the CLOB’s contract storage until they are garbage collected, which doesn’t happen automatically and contributes to the chain’s state bloat. Garbage collection can be expensive if multiple such orders pile up. Furthermore, it is unclear who should bear the costs. As a guiding principle, if Alice’s order has turned stale, Bob should not have to pay Alice’s cleanup costs.

Three solutions leveraging scheduled transactions in different ways:
- On-chain keeper flow: the CLOB runs periodic cleanup jobs, paying for the costs from its own contract’s account and charging the users at placement time to cover them. The main drawback here is that it is unclear how often these jobs need to run (too often is wasteful), but the CLOB can implement estimation mechanisms to address this.
- Reactive scheduling flow: the CLOB can implement the following logic at order placement time: when Alice places her order through its contract, the contract schedules a cleanup transaction at Alice’s order’s due date, with Alice paying for it as part of her call. This transaction is owned by the CLOB, so it cannot be adversarially canceled by Alice. If Alice’s order is fulfilled or canceled, the CLOB cancels the cleanup transaction.
- User-side scheduling flow: the CLOB keeps its current interface, where all orders are Good-Till-Canceled (GTC). To submit a GTD order, Alice sends a GTC order in block
n1and, at the same time, schedules a transaction canceling it in blockn2. This works with an unmodified CLOB, unaware of the scheduled cancellation, but comes with two drawbacks. First, if the order fills beforen2, the cancellation still fires as a wasted no-op unless Alice cancels it herself; the CLOB cannot do this for her, since Alice owns the scheduled transaction (short of a rather sophisticated smart-account flow where the CLOB calls into her delegated EOA). Second, the expiry time can only be as precise as scheduling capacity allows: if blockn2’s scheduled capacity is already full when Alice places her order, she has to pick a later block. Once booked, though, the cancellation is guaranteed to run at that block.
Looking Forward
Scheduled transactions add a capability the EVM lacks: the ability to prepay for and commit to an action ahead of time. The scheduled action becomes visible on-chain, with execution at the target block guaranteed by the protocol unless canceled. For contracts performing scheduling, anyone can audit the conditions under which cancellation is allowed, since the logic is visible in the contract code. A scheduled transaction can itself schedule further transactions, so the same mechanism enables one-off actions and cron jobs alike. Users get access to recurring payments and vesting payouts, as well as staking rewards that compound automatically and approvals that expire on their own. For applications, scheduled transactions remove the hidden keeper assumption behind many on-chain apps. They enable patterns that were awkward or impossible before, ranging from self-funding cron jobs and settlements at a fixed block without a crank to computations that span multiple blocks.
In the forum post, we also discuss several extensions on top of the core design, notably scheduling into the earliest or cheapest block within a window instead of one exact block (making retry logic easier to write); a cheaper best-effort mode that uses spare block capacity for tasks that are not time-critical; and scheduling by time rather than block height, a natural fit for interaction with the Web2 world. Tell us which of these matter most for what you are building!