
Every payment settles as one unit
Atomic transfer sets
One money primitive moves up to 256 legs, all applied in order or rolled back together.
Describe a payment as a set of legs, and the kernel checks the whole set before any balance moves. It matches debits against credits per asset, computes a transfer-set root, and journals every account it touches. If one leg fails, the journal rolls the set back and you get the failing leg index with an error code. Modules request money through the same primitive, so a handler cannot write a balance on its own.
What it does
LayerX has a single money primitive: a transfer set made of legs, where each leg is a from account, a to account, an asset, an amount, a reason code and a supply mode. The kernel checks the whole set, computes a Merkle root over it, journals every touched account, then applies the legs in order. If any leg fails, the journal rolls the set back completely and the caller gets the failing leg index and the error code. Modules request money movement through this primitive; a module handler that tries to write a balance itself is refused.
Key capabilities
- Up to 256 legs per set, applied in order, rolled back together on any failure.
- Per-asset conservation check: debits must equal credits before any balance moves.
- Merkle transfer-set root computed over all legs before the first balance change.
- Journal snapshots balance, asset, and next sequence per touched account for exact rollback.
- Supply modes
CONSERVED,CREDIT_ONLYandDEBIT_ONLYcover transfers, mints and burns. - Failure reports the original leg index and error code rather than a generic reject.
Who it's for
Agent developers and module authors who need multi-party payments to settle as one unit.
Why it matters
A payment that pays a provider, takes a protocol fee and tops up an escrow either happens in full or leaves no trace, so an agent never has to reconcile a half-applied settlement. Because there is one primitive, every money movement carries the same conservation check, the same reason code and the same transfer-set root in its receipt.
Differentiators
Value movement has exactly one code path: the kernel refuses a set if it carries a client-supplied balance (CLIENT_SUPPLIED_BALANCE, -410), refuses a module that already applied a transfer within the same activity (BALANCE_BYPASS, -408), and module handlers that would write a balance directly return LXP_ERR_MODULE_MAY_NOT_WRITE_BALANCE (-722). Rollback also restores the authority scope that a debit already charged, so a failed set does not consume allowance.
Outcomes
- Pay a provider, a fee and an escrow in one settlement
- Never reconcile a half-applied payment, because a failed set leaves no trace
- Read the failing leg index and error code instead of a generic reject
Technical notes
- Limits:
LXP_MAX_TRANSFER_SET_LEGS = 256; the journal holds up to 512 account snapshots; the kernel passes at mostLXP_KERNEL_MAX_TRANSFER_ASSETS = 64asset states into a set. - Amounts are unsigned 128-bit. Conservation is accumulated in 256 bits per asset; a mismatch returns
CONSERVATION(-405). - Zero-amount legs are dropped before the set is applied; a set with nothing left returns
ZERO_AMOUNT(-401). Dropped legs do not shift the reported failure index. - Each leg is hashed from a fixed 115-byte encoding: supply mode, from id, to id, asset id, amount big-endian, reason big-endian. The tree pairs a lone node with itself.
- Per-leg preconditions: asset registered and not paused, asset match on both accounts, neither account frozen, custody and authority checks, allowance, sequence, idempotency and expiry. Ledger error codes run -400 to -418.
- Reason codes are a fixed list of 23, from
LXP_REASON_PAYMENT(1) toLXP_REASON_STORAGE_OCCUPANCY(23). - The actor's sequence advances once per set, not once per leg.
- The transfer-set root appears in the receipt, so a client can check which legs a receipt covers.
// npm install layerx-sdk (Node.js 22+)
import { verifyReceipt } from "layerx-sdk";
const verified = await verifyReceipt(canonicalReceipt, authorized);
const setRoot = verified.receipt.transferSetRoot;
for (const effect of verified.receipt.effects) {
if (effect.monetary) {
console.log(effect.moduleId, effect.transferSetRoot);
}
}More in LayerX agent domain
Asset registry and custody-backed assets
Register an asset, then mint and burn
ExploreCustody bridge and emergency exit
Deposits proven, withdrawals settled on chain
ExploreEscrow holds, capture and disputes
Pay on delivery, not up front
ExploreFees and resource metering
Every activity costs 1/10 of a cent
ExploreBuild on Paxeer.
Give your agent a wallet, set its policy, and settle your first call on LayerX.