LithosDocs
ProtocolPayoutsTokenStatsDocs
Start mining
Documentation home
Protocol
  • Lithos Protocol
  • LFSM
    • Rollups
    • NISPs
    • Emissions
    • Mining on Lithos
    • Candidate building
    • Ergo Voting
  • LithosDex
    • Trading with orders
    • Providing liquidity
Run Lithos
  • Startup guide
    • Setting up an Ergo testnet node
  • Configuration
    • Node connection and security
    • Stratum and difficulty
    • Block packages
    • Batching and orders
    • Collateral and emission
    • Wallet and consolidation
    • Statistics and web panels
    • Synchronization and storage
    • Tasks and dispatchers
    • Failure modes
    • Reporting issues
Client API
  • Overview
    • getLithos client information
    • getList UTXO ids for blocks
    • getUTXO ids by block height
    • getBlock info by UTXO id
    • postBlock info for multiple UTXO ids
    • getGet a block's miner set
    • getList all pool blocks
    • getGet Lithos payouts
    • getStatistics overview
    • getMining and payment totals
    • getMining statistics by hour or day
    • getEstimate Lithos hashrate
    • getNetwork difficulty by epoch
    • getThis client's own hashrate
    • getThis client's hashrate over time
    • getThis client's payouts and claims
    • getCollateral inventory
    • getLocal mining activity
    • getCollateral market snapshot
    • getList queue boxes
    • getList the active set
    • getPermit price history
    • getYour locked block rewards
    • postClaim unlocked block rewards
    • postQuote joining the queue
    • postJoin the collateral queue
    • getLithosDex pool state
    • getLithosDex fee vault state
    • getList your outstanding orders
    • postQuote a swap order
    • postPlace a swap order
    • postQuote a deposit order
    • postPlace a deposit order
    • postQuote a redeem order
    • postPlace a redeem order
    • postCancel an order
    • postQuote a swap
    • postQuote a swap by desired output
    • postExecute a swap
    • postQuote a liquidity deposit
    • postDeposit liquidity
    • postQuote a liquidity redemption
    • postRedeem liquidity
    • getList liquidity provisions
    • postClaim settled fees for a provision
    • postQuote a provision resize
    • postResize a provision
    • postCheck a pending fee flush
    • postFlush pending fees to the vault
    • getPool-wide fee history
    • getPool price history
    • getRecent pool activity
    • getFee history for one provision
    • getWallet balances and lending status
    • getGet Stratum information
    • getGet candidate settings
    • getGet difficulty commitment
    • postCommit a difficulty
    • getGet best NISP at height
Lithos

A decentralized mining pool protocol on Ergo. Miners build their own blocks; contracts pay them for proven work.

Live on Ergo mainnet
Protocol
  • How Lithos works
  • Mining on Lithos
  • NISPs
  • Emissions
  • LFSM contracts
Run Lithos
  • Startup guide
  • Configuration
  • LithosDex
  • Client API
  • Failure modes
Network
  • Live stats
  • Public stats API
  • Ergo Platform↗
  • SOAT Miner↗
Community
  • Telegram↗
  • X / Twitter↗
  • GitHub↗
Lithos
© 2026 Lithos Protocol. Open source under the MIT licence.λίθος — stone
Docs/Run Lithos/Configuration

Batching and orders

Filling other people's DEX orders for their executor fee, and the fees your own orders carry.

Advanced8 min readAssumes you know what a DEX order box is and how mempool chaining works. Start with Block packages if not.

A batcher finds orders that other people have placed, executes them correctly, and keeps the executor fee the order's own script pays. This page covers the batching block, which configures the two batchers, and lithosdex.orders, which sets the fees on orders you place.

How a batcher earns#

An order is a box whose script pays a fixed executor fee to whoever fills it correctly. The contract fixes the price bound, the owner's reward script and the fee. You choose the moment and keep the fee.

Nothing about filling an order needs your own capital. No path selects a wallet box: a run is funded entirely by the orders it fills, and its takings chain from one execution to the next.

There are two ways to execute, and the difference is the miner fee:

Candidate, in your own blockBroadcast, through the mempool
Miner feeNonemin(order cap, broadcastMinerFeeCeiling) out of the executor fee
Pool boxConfirmed onlyFollowed to its mempool tip
Competing spendsSupersededNever displaced
ShipsOn for bothOn for both

In your own block an execution pays no fee at all, so the whole executor fee is revenue. A broadcast has to pay the network, so its revenue floor is higher. To fill orders only in your own blocks, set batching.<name>.broadcast = false.

Note
Two switches must both be on for candidate execution

batching.<name>.enabled runs the batcher, which scans and can broadcast. Inserting executions into your own block additionally needs stratum.candidate.blockTransactions and the matching stratum.candidate.sources.<name> source. A batcher runs whether or not this node mines, because broadcasting needs no stratum.

Settings both batchers share#

These keys exist under both batching.ergodex and batching.lithosdex:

SettingDefaultRangeWhat it does
enabledtruetrue or falseRun this batcher at all
scanIntervalMs80005000 to 3600000 msMilliseconds between confirmed-order scans. Each pages the node's indexer
maxTrackedOrders5121 to 4096Order ids retained between scans. Boxes are reloaded before execution
maxOrdersPerBlock200 to 100Transaction slots per candidate build or broadcast pass, carried ancestors included
minRevenueNanoErg10000000 to 1000000000000Minimum gross revenue per fill. An opening fill must also fund a takings box
broadcastMinRevenueNanoErg25000000 to 1000000000000Minimum revenue for broadcasts. Net takings must stay positive after the miner fee
broadcastMinerFeeCeiling20000000 to 100000000Most of the executor fee a broadcast may spend as a network fee, further limited by the order's own cap
broadcasttruetrue or falseBroadcast fills to the mempool rather than only filling in your own block
broadcastMempoolOrderstruetrue or falseWith broadcast on, also broadcast orders still unconfirmed
skippedOrderTtlMs360000060000 to 86400000 msHow long an order that priced but could not be built is left out
maxSkippedOrders40960 to 100000Such orders remembered at once, oldest forgotten first. About 200 bytes each
maxAncestorTxs20 to 100Unconfirmed transactions one order may need carried ahead of it. 0 executes confirmed orders only
maxMempoolOrders640 to 4096Unconfirmed orders one pass takes
maxMempoolOrdersPerTx41 to 4096Unconfirmed orders any single transaction may contribute
maxUnbuildablePerRun321 to 1000Orders a run fails to build before it stops trying more
maxUnbuildablePerTx21 to 1000Orders from one transaction a run fails before passing over that transaction's others
strategymaxFeesmaxFeesHow each run chooses which orders to execute against a pool, and in what order
searchBudgetMs2500 to 10000 msMost time the strategy may search one plan. 0 plans greedily without searching

deniedPools also exists on both as a list of pool NFT ids never to execute against. It defaults to empty and is not validated at startup. Ids are compared lowercased.

Choosing which orders a run executes#

Every fill moves the pool's price, so the order a run fills orders in changes what each one pays. One order can push another below its minimum, or bring it back within it. strategy names how a run settles that, and maxFees is currently the only strategy.

maxFees looks for the set and sequence of orders that earns you the most:

  1. It starts from a greedy plan that re-prices every remaining order after each fill.
  2. It then tries dropping each planned order and inserting each unplanned one at every position, and keeps any change that earns strictly more.
  3. Between plans that earn the same, it keeps the one that leaves the price nearest where the run opened, since that keeps more of the other orders within their minimums.

The search considers at most 64 orders per pool, the most valuable first, and stops after searchBudgetMs. It never takes more than a quarter of the time left to build, whatever the budget, so a deep order book cannot hold up your block. Startup refuses a strategy name it does not know.

Raising searchBudgetMs can find a better plan on a busy pool, at the cost of build time on every run. The first plan is ready long before the budget runs out, so a budget of 0 still executes orders. It only skips the search for a better set.

Executing unconfirmed orders#

An order placed seconds ago is worth more than one that has sat for a block, so both batchers read the mempool themselves and carry the transaction that placed an order ahead of the execution. That is where most of the revenue is, and three rules keep it safe.

A placement must be a wallet's, not a bot's. Its inputs must be uncontested and spendable by one key alone, with no data inputs and no pool output. A transaction that executes an order and places a new one fails this test, so a competitor's work is never carried.

Depth is bounded by maxAncestorTxs. Each carried transaction is one more that can invalidate your whole run.

Spam is bounded per creating transaction, not per order. Order boxes are cheap to make and transactions are not, so unconfirmed orders are taken in rounds across the transactions that created them. maxMempoolOrdersPerTx caps what any one transaction contributes, so a transaction carrying hundreds of order boxes takes four places rather than all of them.

The two maxUnbuildable settings work the same way for failures. A run stops after maxUnbuildablePerRun failures in total, and passes over a single transaction's remaining orders after maxUnbuildablePerTx of them fail, so one spam transaction cannot use up the whole run's attempts.

Note
What a failure costs you is fees, never funds

An order that cannot be built is passed over and skipped for an hour. A run that overruns its budget offers nothing. An unbuildable order leaves the owner's funds in their own refundable box. Each failure is one wasted signature, which is why the bounds exist at all.

ErgoDEX only#

ErgoDEX executes against many pools, so it bounds how many it tracks and skips idle ones:

SettingDefaultRangeWhat it does
batching.ergodex.maxTrackedPools2561 to 1024Pools tracked, newest first
batching.ergodex.maxPoolAgeBlocks201601 to 10000000Skip pools whose current box was included more than this many blocks ago

The ErgoDEX batcher covers MewDex too, since MewDex is the same contracts under a different interface.

Its candidate source ships on, so ErgoDEX executions reach your blocks. To keep them out, set stratum.candidate.sources.ergodex.enabled = false. The batcher keeps scanning to broadcast, since batching.ergodex.broadcast ships on. Set that to false as well and it stops scanning.

LithosDex only#

LithosDex always executes against the ERG:LIT pool. Anyone can deploy another LithosDex pool from the same contracts, and the batcher can serve those too:

SettingDefaultRangeWhat it does
batching.lithosdex.autoFlushtruetrue or falseClose each candidate run with a flush, moving the pool's pending fees to the vault
batching.lithosdex.discoverPoolstruetrue or falseAlso execute orders for LithosDex pools other than ERG:LIT, in your own blocks and by broadcast
batching.lithosdex.maxTrackedPools2561 to 1024With discoverPools on, the most pools served besides ERG:LIT, newest first

A flush is what lets liquidity providers claim the fees the pool has accumulated. It pays you nothing and costs nothing in a block besides the small space it takes up in size and cost. It is enabled by default to ensure permanent fees are moved quickly. It is never broadcast, because a broadcast flush would pay a network fee for no return.

A discovered pool is served only when it checks out. The client rebuilds the pool and vault contracts from the pool's own token ids and requires them to match what is on chain exactly. A pool that fails the check is ignored, so a look-alike box cannot draw your executions. To exclude a pool that passes, add its NFT id to batching.lithosdex.deniedPools. For what LithosDex is and how its orders work, see LithosDex.

Fees on orders you place#

The lithosdex.orders block sets the fees carried by orders you place through the API or web panel, when the request does not name its own:

SettingDefaultRangeWhat it does
lithosdex.orders.executorFeeNanoErg3000000Checked per requestWhat your order pays whoever fills it
lithosdex.orders.maxMinerFeeNanoErg1000000Checked per request, must be below the executor feeMost of that fee a filler may spend as a network fee

Both are paid only when a miner fills the order. A cancelled order pays neither.

These are validated per request rather than at startup, so a bad value refuses the order rather than refusing to start the client.

The default executor fee is set to clear the shipped floor of every miner running this client. Miners skip orders paying less than their minRevenueNanoErg, so an order priced below that waits indefinitely rather than failing.

Next steps#

  • Block packages: the source budgets that gate candidate execution.
  • Collateral and emission: the other thing the client spends on your behalf.
Edit this page on GitHubsynced from e0ca86a
← PreviousBlock packagesNext →Collateral and emission
On this page
  • How a batcher earns
  • Settings both batchers share
  • Choosing which orders a run executes
  • Executing unconfirmed orders
  • ErgoDEX only
  • LithosDex only
  • Fees on orders you place
  • Next steps