AXIS Docs

Developers

Design rationale

Why AXIS matches off-chain and settles on-chain, holds no funds between calls, lets clients choose IDs, skips instead of failing and charges no fees, and what that costs.

AXIS is shaped by the limits of Soroban. Per-transaction budgets are generous, but contract event bytes, ledger write bytes and storage rent are scarce, and two transactions that write the same ledger entry cannot run in parallel. Each decision below answers one of those constraints. Resource limits and costs has the numbers.

Off-chain matching, on-chain verification#

An orderbook that matches inside the contract needs a price-sorted index in contract storage. Every order then adds index entries that pay rent, every trade rewrites index entries shared by everyone trading that market, and the matching loop has to fit inside one transaction's budget.

AXIS splits the work. Finding the best orders is a search over the whole book, so it happens off-chain, where it costs nothing and where any number of routers can compete. The contract only verifies what a caller proposes (each listed order's price, expiry, the maker's backing and the maker's ability to receive) and settles. What this buys:

  • One ledger entry per order, about 250 bytes, and no index or price level entries.
  • No shared index entries. Trades on different makers share only the contract's balance entry of the asset they buy.
  • No matching loop over orders the taker never wanted.
  • Flexible execution: a taker can leave out dust orders, and a large trade can collect liquidity from the whole book.
  • An open playing field: the indexer is open source, and the contract treats every router the same.
  • A small attack surface: the code that moves funds is a few settlement functions, which are easier to review than a full matching engine.

No custody#

The contract never holds user funds between calls. Each order is backed by its owner's balance and a standing allowance, and each fill is settled by transfer_from calls with the contract as the spender: the taker pays the maker directly, and the maker's asset passes through the contract to the taker before the call ends. Why:

  • Nothing to drain. No call leaves the contract holding more than it held before, so a freeze strands nothing.
  • Signatures that survive the book changing. The taker signs the call and at most an approve with an absolute amount. Makers are paid under the contract's own authority, so a signed trade stays valid when listed orders move between simulation and inclusion.
  • Faulty makers become skippable. A spent balance, an expired allowance or a deauthorized trustline affects only that maker's fills.
  • Faults land on the right side. Collecting each maker's asset into the contract before it reaches the taker separates the two sides of a fill: a maker whose asset cannot be collected is skipped, and a taker who cannot receive fails the call with CannotReceive instead of getting every maker flagged. A payment a maker cannot be credited with is the one failure the contract cannot attribute, so it fails the call (see Failure modes).
  • Cheap order management. Creating, updating and removing orders moves no tokens.
  • Capital efficiency. Funds behind open orders stay liquid in the maker's wallet. The same inventory can back orders on several markets, quote RFQs with immediate settlement or earn yield elsewhere, without canceling orders or withdrawing anything from the contract.

The costs are that an order is a promise, not a reservation, and that trades buying the same asset cannot run in parallel, since each one writes the contract's own balance of that asset. See Settlement and allowances.

Client-chosen order IDs#

An order ID is the first 16 bytes of sha256(xdr([owner, nonce])), with a u64 nonce the client picks. An on-chain counter would be written by every order creation and serialize them all. With a client-chosen ID the client knows the key of the order its trade may create before simulation, so it can declare that entry in the footprint up front, and apps can track their orders before the indexer reports them. The costs: IDs are not sequential, a nonce must be unique among the owner's live orders (OrderExists, 711), and indexers order orders by event position instead of ID. See Contract design.

Skip instead of fail#

Between a client's simulation and the ledger that includes its transaction, other takers fill orders, makers reprice them and balances change. If every unusable order failed the whole transaction, takers would lose fees and races for reasons outside their control. So the contract skips: a missing, expired or worse-priced order silently, and an order whose maker cannot settle with an 84-byte skip event, leaving the order unchanged. Matching never trims, caps or removes an order it cannot fill, so a maker whose wallet is briefly short does not lose the order, and a skip costs an event instead of a storage write. Indexers use skip events to stop proposing makers that fail to settle.

Two calls keep strict semantics on purpose: Fill-or-Kill fails with NotFilled (709) unless it executes in full, and swap fails unless the route meets the bounds the trader signed.

Oracle-protected markets and a minimum order value#

Anyone can open a market with subsidize, if the Reflector oracle quotes at least one of its assets, by burning the listing fee in XRF, which buys listing_min_days days of the oracle's price feeds for the market. AXIS receives nothing: the oracle burns the XRF. Why gate markets at all:

  • Spam resistance. Every order costs every indexer and router some work, and the state of the network some bytes, so dust orders that clog the book are a problem for any decentralized exchange. The Classic DEX answers it with 0.5 XLM of reserve locked per offer. AXIS keeps dust out with a minimum order value in USD, which needs a USD price for at least one side of the market, and the feeds provide it. Requiring an asset the oracle quotes also keeps trash tokens and wash-trading pairs out.
  • No gatekeeper. Paying for the feeds replaces an admin decision, and the safety admin has no call to list or delist a market.

The oracle never prices a fill. trade and update value new and changed orders from a price cache that keepers refresh with requote, and never call the oracle. Takers need no market at all: market orders, Fill-or-Kill trades, swaps and crossfills work on any pair with orders. See Markets and oracle.

No protocol fees#

AXIS charges no trading fee and has no fee switch. Makers receive exactly their price, and takers pay exactly the maker's price, apart from rounding of under one base unit per fill. A fee would need either one more token transfer per fill, adding an event to the budget that already limits fills per transaction, or a fee balance the contract keeps between calls, which brings back custody and something an admin would have to withdraw. Without a fee switch there is nothing to govern and nothing an admin could change.

Compact events#

The binding per-transaction limit is the 16,384-byte cap on contract events. A fill against a distinct maker emits one trade event of about 320 bytes and two token transfer events of about 236 bytes each. The token's events are outside AXIS's control, so AXIS keeps its own share small: multi-field events use the positional vec format without field names, a fill emits no order event (the trade event carries left, the order's new amount), trades and swaps have no on-chain ID, expiry emits nothing, and a skip is a single order ID. The result is 20 fills per trade. See Events.

Trade-offs accepted#

  • Per-fill cost. Each fill against a distinct maker writes 4 ledger entries and emits 792 bytes of events, so a trade filling 20 orders costs about 0.032 XLM in execution fees (mainnet, September 2026). Settling every maker from their own wallet is what removes custody, and it is also what makes each fill cost this much.
  • 20 fills per transaction. The event cap allows 20 fills per trade, 20 across all hops of a swap and 19 maker fills per crossfill. A sweep through more orders takes several transactions. The contract stores no such cap, so the limits rise if the network raises its event cap.
  • Orders can be unbacked. An allowance reserves nothing. A maker can spend the balance or let the allowance expire, and the order stays on the book. The contract re-checks backing at every fill, and the AXIS API counts and proposes only backed amounts.
  • No on-chain priority. The contract matches the orders a taker lists, in the taker's order. Routers compete on execution quality instead, and the AXIS API routes best price first.
  • Crossed books. A Limit trade only sees the orders it lists, so its remainder can end up crossing orders it never listed. A crossed book is an open arbitrage opportunity: anyone can match the crossing orders with crossfill and keep the difference.
  • Self-trades. The contract does not compare the taker with the makers. This is deliberate: the taker chooses the orders, so it decides whether to list its own.
  • Rent per order. Every new order pays about 0.041 XLM of ledger rent up front, not refunded, and the rate rises as network state grows. update reprices orders in place without a new rent chunk, which is why market makers should requote rather than recreate. An order nobody updates archives once its lifetime runs out. See Order lifecycle and rent.
  • No upgrades. The contract is non-upgradeable, so a fix or a new feature needs a new deployment, and makers move their orders and allowances to it. This is the price of code that nobody can change under open orders. See No upgrade path.

How AXIS compares#

The Stellar Classic DEX matches inside the protocol with price-time priority, and offers are always funded because liabilities lock the funds in the account. AXIS is a smart contract that other Soroban contracts can call. Its takers choose the orders, its makers' funds stay spendable, orders can expire, and the rent is not returned. The two venues have separate liquidity.

AMMs price trades with a formula over pooled reserves. On AXIS, makers set exact prices and sizes and keep their funds in their own wallets, with no pooled custody. In exchange, liquidity exists only where makers quote, and it depends on makers and keepers staying active.

Orderbooks that match on-chain enforce priority and never cross, but every order and trade writes shared index state, and the matching loop must fit one transaction. AXIS drops the index: orders are cheaper, unrelated trades run in parallel, and routing improves off-chain without contract changes. It gives up on-chain priority and accepts crossed books, which crossfill turns into an arbitrage opportunity.

Architecture shows how the parts fit together.