AXIS Docs

DevelopersSmart contract

Order lifecycle and rent

How an order moves from creation to removal, which event marks each step, and how long its ledger entry and the contract itself stay alive.

States and transitions#

Transition Caused by Event Storage
Created A Limit trade stores its remainder new with ID, owner, price, amount and expiration Entry written
Partially filled A fill leaves an amount that is not dust trade with left > 0 Entry rewritten
Filled A fill takes the whole order, or leaves dust trade with left = 0 Entry deleted
Updated update with a non-zero amount mod with the new price, amount and expiration Entry rewritten, TTL extended
Removed update with amount 0 mod with amount 0, price and expiration unchanged Entry deleted
Expired The ledger time reaches expires None Entry kept, reads as gone
Revived update of an expired order with a non-zero amount mod Entry rewritten, TTL extended
Skipped A listed fill its maker cannot settle skip with the order ID Unchanged
Archived The entry's TTL runs out None Archived, a restore is needed to touch it

A fill emits only the trade event: its left field is the amount stored after the fill, so an indexer updates the order from the trade alone. When an order serves as the taker order of a crossfill, its own fill is reported the same way, with the caller as taker. Event layouts are in Events.

Order states: created by a Limit trade with a new event, live, partially filled through trade events with left above zero, filled and removed at left zero, updated with mod, removed by update with amount 0, expired without an event and revivable by update, archived once its lifetime runs out without updates, and skipped fills that leave the order unchanged.

Expiration#

expires is a UNIX timestamp in seconds, and 0 means the order never expires. An order is expired once expires <= now, where now is the ledger close time.

  • A Limit trade sets the expiration of the remainder it stores, and update changes it (0 lifts it). Both reject a value that is not in the future with InvalidExpiration (707). Fill and FillOrKill trades ignore the argument.
  • The check runs at execution time. An expiration only a few seconds ahead can already be in the past when the transaction is included, so leave a margin.
  • From the moment it expires, the order reads as gone everywhere except in update. The order view returns None, matching skips it without an event (in trade, swap and as a crossfill maker), crossfill with it as the taker order fails with OrderNotFound (710), and its ID is free for a new order.
  • Expiry emits no event and writes nothing. The entry stays until its owner removes or revives it, a new order overwrites it, or it archives. Indexers expire orders on their own clock, from the expires of the latest new or mod, and keep the record because the order can still be revived.

Reviving an expired order#

Only the owner can act on an expired order, and only through update:

  • An amount of 0 removes it and emits mod with amount 0. This works while the contract is frozen too.
  • A non-zero amount revives it under the same ID with the new amount, price and expiration. The new expiration must be 0 or in the future, and every other update check applies: backing, minimum order value, the dust rule. The call emits mod and extends the entry's TTL.

A revived order fills again like any live order. Until then, an expired order is never filled, crossed or returned by the order view, and no update can leave an order in the expired state.

TTL policy#

The contract sets every lifetime (contract, markets, orders, cached prices) in days or hours and converts it to ledgers with Config.ledger_time, the expected ledger close time, in src/ttl.rs. It is 5 seconds at deployment, that is 17,280 ledgers per day. When the network changes its ledger close time, the safety admin updates it with set_ledger_time (1 to 20 seconds), so entries keep living for the intended time.

Entry Initial lifetime Extended by Extension
Order Network minimum for a new entry: 2,073,600 ledgers (about 120 days) on mainnet, 120,960 ledgers (7 days) on Testnet update that changes or revives it, a new order written over an expired entry To its expiration plus 1 day, capped at 120 days, or 120 days without expiration. Never shortened
Order Fills, removals, reads Never
Market About 120 days: the network minimum on mainnet, and extended to 120 days when the market is opened where the minimum is shorter Every read: requote, subsidize, the market view To 120 days once less than 30 days are left
Market Every order written to it: Limit trades, update that changes an order To 121 days once less than 120 days are left, so the market outlives every order on it
Price cache 3 days Every write by requote or subsidize To 3 days
Contract instance and code Set at deployment Every state-changing call To 3 days once less than 3 days are left
Contract instance and code An ExtendFootprintTTL operation, sent by a keeper To the horizon the keeper picks, capped by the network maximum

An order that nobody updates archives when the lifetime granted at its creation or at its last update runs out, even when it never expires: the network minimum after creation (about 120 days on mainnet, but only 7 days on Testnet), and 120 days or its expiration plus 1 day after an update. A maker who reprices with update keeps the order alive at each update, paying rent only for the time since the previous extension. The market view extends the market entry only when it runs inside a submitted transaction. Reading it through simulation changes nothing.

Archival and restore#

A persistent entry whose TTL runs out is archived, not deleted. Since Protocol 23, a transaction restores archived entries automatically when its footprint marks them for restore:

  • Simulation finds the archived entries a transaction touches and marks them for restore in the transaction data. They are restored as part of the transaction, which pays a fresh rent chunk for each.
  • An archived entry declared read-write but not marked for restore fails the transaction when it is applied.
  • Whoever submits the transaction pays. A taker who lists an archived maker order pays for that order's restore, and an owner who removes or updates an archived order pays to restore it first.

The JS client keeps the restore marks from simulation, so its calls restore the archived orders, markets and token balances they touch, and the simulated fee includes the rent. Footprint completion declares listed orders the simulation did not reach without that mark, so an archived spare order fails the transaction. Routers should therefore not list orders whose lifetime has run out: about 120 days after the last creation or update on mainnet, and on Testnet 7 days after creation when the order was never updated, which the indexer can tell from the creation and update times.

An archived order is not canceled. Anyone willing to pay the restore can bring it back and fill it, as long as its maker still backs it. A maker who wants an order gone for good removes it, or revokes the allowance on the token it sells.

Contract lifetime#

The contract instance (configuration, oracle decimals, frozen flag) and its Wasm code each have a TTL. If they archived, every call would first have to restore them.

  • Every state-changing call extends them only when fewer than 3 days are left, and then only to 3 days.
  • Keepers extend both further with the standard ExtendFootprintTTL operation, with the instance and code entries in its read-only footprint. The contract has no entry point for it. Anyone can send the operation, for any horizon up to the network maximum, and it works while the contract is frozen. AxisContractClient.keepalive(days) in the JS client builds and sends it.

The rent of the code entry dominates, about 2.5 XLM per day of lifetime on Testnet in October 2026. As long as keepers extend the contract, traders pay no contract rent. Without a keeper, every state-changing call that finds less than 3 days left tops the contract up to 3 days and pays the rent of the time since the previous top-up. Running a keeper has the cost figures.

Rent figures#

A September 2026 mainnet snapshot, at the minimum rent rate of 1,000 stroops per KB:

Item Cost
New order entry (about 250 bytes for 120 days) About 0.041 XLM, not refundable
Creating an order, execution fee included About 0.042 XLM
Restoring an archived order A fresh 120-day rent chunk, about the cost of a new entry, paid by whoever restores it
First token balance of a contract address (a smart wallet, or the AXIS contract itself for every asset bought through it) About 0.037 XLM, once per address and token
update extension Proportional to the time since the previous extension

The rate rises up to 10 times as network state grows, so all rent figures can move. Rewriting an order in place with update pays no new chunk, which is why requoting with update costs a fraction of removing and recreating orders. Resource limits and costs has the full fee tables.