Asentum

Build on Asentum

Scheduled Contract Calls

Native cron on chain. Your contract fires itself on a schedule, no keeper needed. Estimated read time: 6 minutes

TL;DR

AsentumChain has a system contract at 0x...05 that maintains a calendar of scheduled jobs. Any contract can call cron.schedule to register a future call against itself. On every block, the chain checks the calendar and fires any due jobs inside the same apply path that runs normal txs.

No off-chain keeper. No oracle. No bot you have to keep running. The chain itself is the scheduler. The job is paid for upfront in gas escrow at schedule time, billed against the escrow on each fire.

Schedule a call

function init() {
  // Charge subscribers every day at midnight UTC
  cron.schedule(
    this.address,                  // target contract
    'chargeAll',                   // method to call
    [],                            // args
    chain.timestamp + 86400,       // first run unix seconds
    86400,                         // interval seconds (0 = one-shot)
    0,                             // max runs (0 = forever)
    500_000n                       // gasLimit per fire
  );
}

function chargeAll() {
  // The chain calls this on schedule. msg.sender is the zero address
  // for chain-initiated calls, so check it if you want to gate.
  for (const [addr, sub] of storage.entries('sub:')) {
    if (chain.timestamp >= sub.nextCharge) {
      E(ASE).transferFrom(addr, storage.merchant, sub.amount);
      sub.nextCharge += 86400;
    }
  }
}

The first run lands in the block whose timestamp is greater than or equal to nextRunAt. After that, the registry advances nextRunAt += intervalSec and re-inserts the job in the due index. One-shot jobs (interval 0) are removed after their first fire.

Cron expressions

For convenience, the SDK exposes helpers that translate familiar cron expressions to {nextRunAt, intervalSec} pairs. From within a contract, you can use sugar like:

cron.schedule(this.address, 'chargeAll', [], '@daily',  0, 500_000n);
cron.schedule(this.address, 'reportIn',  [], '@hourly', 0, 200_000n);
cron.schedule(this.address, 'firstOfMonth', [], '0 0 1 * *', 0, 1_000_000n);

Sugar gets expanded in the registry contract itself, so the chain only sees concrete (nextRunAt, interval) pairs. Same gas semantics either way.

Gas escrow

When you call cron.schedule, you attach native ASE as gas escrow. On each fire, the chain bills the actual gas used (capped at gasLimit) against the escrow at the prevailing base fee. When the escrow can no longer cover one more fire at the cap, the job is removed and the remainder refunded.

This means the chain is incentive-compatible to run your job: it's being paid for the gas. No reliance on altruistic keepers, no MEV games for who fires first. Just regular gas, paid upfront, billed per fire.

Cancel a schedule

cron.cancel(jobId);   // refunds remaining escrow to msg.sender

Only the original scheduler can cancel. The remaining escrow goes back to whoever paid it. If you scheduled from inside a contract (typical), only that contract can cancel.

With Vault and Workers

Cron plus Vault and Workers is the killer combo. Schedule a method on your contract via cron. Have that method call Vault.requestDecryption with a payload telling the Worker to hit Slack, Resend, OpenAI, whatever. Now you have a contract that autonomously triggers real-world API calls on a schedule, with the schedule, the authorization, and the credential all on chain.

No other blockchain has this combination natively.

Limits

  • Per-block cron budget: the chain runs jobs until cumulative gas hits CRON_BLOCK_GAS_BUDGET (currently 10M). Excess jobs slide into the next block. Protects block production from runaway cron storms.
  • Minimum interval: 60 seconds. Anything shorter is rejected at schedule time.
  • Max jobs per block from one contract: 100. Helps fairness when many contracts share the budget.
  • Failed job billed anyway: if your method throws, the chain still bills the gas used. The chain logs the failure and reschedules; your contract should be idempotent.

Full source for the cron registry lives at packages/node/src/chain/cron-registry.ts in the AsentumChain repo.