Transactions
Every change to the MLX ledger is a transaction signed by a wallet's Ed25519 key or by the sequencer, ordered into hash-linked, signed blocks.
What a transaction is
A transaction is a small JSON object. The wallet signs it, sends it to POST /api/tx, and the sequencer applies it to the ledger in a block. Nothing else changes a balance.
{
"chain": "mlx-1",
"from": "luca.mlx",
"nonce": 4,
"ts": 1793116800000,
"pk": "7812b1435b317df04f45731ac508ae7774d4731ee0445fd46ccdcc5019faa524",
"body": { "type": "transfer", "to": "shop.mlx", "token": "USDC", "amount": "2500000" },
"sig": "ce8c7621b38777895b06107168e08a542bf9d5fbaf2ac53bb1b9a1b1d3ade2635d5c00150a2f76990b49330365d964c2b55d3289bbc9cc3e380550a2715cb10f"
}| Field | Type | Notes |
|---|---|---|
chain | string | The network's chain id. Production is mlx-1; GET /api/stats returns it as chain. |
from | string | The wallet that signs, for example luca.mlx. sequencer for system transactions. |
nonce | integer | The wallet's next nonce: its current nonce plus one. create_account uses 0. |
ts | integer | When it was signed, in milliseconds since 1970. Must be close to the network's clock. |
pk | string | The signer's Ed25519 public key, 64 lowercase hex characters. |
body | object | What to do. body.type names the transaction type. |
sig | string | Ed25519 signature, 128 hex characters, over the transaction's hash. |
Amounts are strings of base units with 6 decimals: "2500000" is 2.5 USDC.
Hash and signature
The transaction's hash is SHA-256 of its canonical JSON (keys sorted, no whitespace) without sig. The signature is Ed25519 over those 32 hash bytes. The hash is also the transaction's id: GET /api/tx/<hash>. Signing the same fields twice gives the same hash, so sending a transaction again never applies it twice. Details and a worked example: Keys and signing.
Nonces
Each wallet has a nonce, starting at 0 when it is created. A transaction applies only with nonce = current nonce + 1, and a successful one moves the wallet's nonce up by one. A failed transaction does not use its nonce: sign the next one with the same number. The sequencer applies a wallet's transactions in nonce order, and one that arrives ahead of its predecessor waits for it briefly in the mempool. See Submitting.
Read a wallet's current nonce from GET /api/account/<name> (account.nonce).
Blocks
The sequencer takes transactions from the mempool, applies them in order and seals them into a block. A submit seals a block right away when it can; a scheduled job seals anything left. Each block carries:
prev: the hash of the block before it, so blocks form a chain from the genesis block (height 0);txRoot: a Merkle root over the hashes of the transactions it holds;producerandsig: the sequencer's public key and its Ed25519 signature over the block hash.
A transaction that fails is still recorded in its block, with status: "failed" and an error, and changes nothing. Every block then goes into the ledger's transparency log. Block endpoints: Blocks.
User and system transactions
| User transactions | System transactions | |
|---|---|---|
| Signed by | the wallet's own key (pk must be the key the wallet was created with) | the sequencer's key (from: "sequencer", nonce: 0) |
| Examples | create_account, transfer, swap, buy_node, withdraw, bind_device | deposit, mint_node, compute_reward, withdraw_settle, issue, adjust, gift |
| Who sends them | wallets, MLX Node's link sheet, your own code | the network itself: the payment watcher, the hourly reward run, withdrawal payouts, the operator's admin |
System transactions are public like any other: they show on the explorer with their reason, and the hourly state commitment covers the balances they produce. A user cannot send one: a system type signed by a wallet fails.