# Transaction types

Every transaction type the ledger knows, what its body holds, what it does and the rules that make it fail.

Every type goes in `body.type`; the other `body` fields are listed per type. Amounts are strings of base units (6 decimals). Tokens are `USDC` (the network dollar), `MLX`, and `USDT` (older balances only).

Rules marked **door** are checked by `POST /api/tx` before the transaction is stored (an HTTP error, nothing recorded). The rest are checked in the block: a transaction that breaks them is recorded with `status: "failed"` and its `error`, changes nothing and doesn't use its nonce. Every user transaction also has to pass the common checks on [Submitting](/transactions/submitting#errors).

## Account

### create_account

Creates the wallet named in `from` and records `pk` as its key.

| Field | Type | Notes |
|---|---|---|
| `referrer` | string | Required since 29 Oct 2026 16:30 UTC (invite-only): an existing wallet that invited this one, for example `luca.mlx`. |

- `from` is the new name, `nonce` is `0`. **door**
- The name matches `NAME_RE` in `lib/chain/types.ts` (`^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?\.mlx$`: lowercase letters, digits and hyphens, starting and ending with a letter or digit) and is not reserved (`rewards`, `treasury`, `bridge`, `sequencer` and the other protocol names) or a look-alike of one (`treasury1`, `mlx-support`). **door**
- `pk` is a valid Ed25519 key that is not of small order. **door**
- The name isn't taken, and `pk` doesn't already control another wallet.
- `referrer` names an existing wallet that isn't a protocol account or the wallet itself; otherwise "An invite is needed to create a wallet…". **door** (Before 29 Oct 2026 16:30 UTC it was optional, and an unknown one was ignored.)

## Money

### transfer

Moves a token from the signing wallet to another wallet.

| Field | Type | Notes |
|---|---|---|
| `to` | string | The receiving wallet. |
| `token` | string | `USDC`, `MLX` or `USDT`. |
| `amount` | string | Base units, more than zero. |

- `to` exists and isn't the sender ("You can't send to yourself").
- Protocol accounts can't receive transfers, except `treasury.mlx`.
- Only unlocked MLX moves. Locks whose date has passed are released first; MLX still locked can't be sent ("That MLX is still locked…").
- The sender holds the amount ("luca.mlx has insufficient USDC").

### swap

Buys MLX with dollars, or sells MLX for USDC, at the protocol's spot price on the emission curve (the buy premium and sell discount apply; the live spread is in `GET /api/stats`, `market`).

| Field | Type | Notes |
|---|---|---|
| `tokenIn` | string | `USDC` or `USDT` to buy MLX; `MLX` to sell for USDC. |
| `amountIn` | string | Base units of `tokenIn`. |
| `minOut` | string | The least you accept, in base units of what you get. Your slippage guard. |

- Get a quote, and the `minOut` to sign, from `GET /api/market/quote?side=buy|sell&amount=<base units>`. See [Market](/network/market) and [the curve](/protocol/curve).
- Below the smallest buy or sale the swap fails ("The smallest buy is …", "The smallest sale is …").
- A buy fails if the treasury can't deliver that much MLX; a sale fails if `treasury.mlx` lacks the USDC ("Sell liquidity is low right now…").
- Only unlocked MLX can be sold.
- If the result would be below `minOut`: "The price moved beyond your limit. Try again."
- MLX bought on the spot market is never locked.

### withdraw

Takes USDC out of MLX Network. The amount (plus any fee) moves into the escrow account `withdrawals.mlx` at once; a `withdraw_settle` system transaction later records the payout or refunds it.

| Field | Type | Notes |
|---|---|---|
| `token` | string | `USDC`. |
| `amount` | string | Base units, at least 10 USDC. |
| `chain` | string | `solana`. |
| `address` | string | Where to pay. |

- **Solana**: `address` is a Solana wallet address (base58). A USDC token account's address is refused ("That's a token account's address…"). **door** The network fee is 0.5 USDC, taken with the amount ("You need … including the … network fee").
- A refund (`rejected`) returns the amount and the fee.

## Compute API

### api_fund

Adds [API credit](/compute/credit-and-billing): USDC from the wallet into the protocol account `api.mlx`, held for the wallet's Compute API requests.

| Field | Type | Notes |
|---|---|---|
| `amount` | string | Base units, at least 1 USDC; the wallet's credit stays at most 1,000 USDC in the pilot. |

### api_refund

Gives API credit back to the wallet's USDC.

| Field | Type | Notes |
|---|---|---|
| `amount` | string | Base units, at most the wallet's `apiCredit`. |

`POST /api/tx` also refuses (409, with the amount you can take back) a refund of credit that pays for requests since the last daily bill or for requests running now. **door**

## Seats

### buy_node

Buys a dedicated seat from the wallet's balance. (Seats paid with crypto at checkout arrive as the system transaction `mint_node` instead.)

| Field | Type | Notes |
|---|---|---|
| `tier` | string | The seat: one of the ids below. |
| `device` | object | The Mac it's meant for, as the browser saw it: `class` (`mac`, `iphone`, `ipad`, `other`), optional `chip`, `appleSilicon`, `cores`, `memoryGB`, `screen`, `model`. Unknown or malformed fields are dropped. |
| `pay` | string | Optional. `USDC` or `USDT` to pay from that coin only; `WALLET` to pay from the whole balance. Left out: USDC, else USDT. |
| `maxMlx` | string | With `pay: "WALLET"`, when MLX has to be sold: the most MLX (base units) you allow to be sold. |

| Seat | Id | Price | Units | MLX a day (cap) | MLX an hour | Mined MLX locked | Full rate on |
| --- | --- | ---: | ---: | ---: | ---: | --- | --- |
| Nano | `N` | $50 | 0.5 | 4 | ~0.17 | 20 days | any Mac |
| Lite | `T` | $100 | 1 | 8 | ~0.33 | 14 days | any Mac |
| Core | `S` | $250 | 2.5 | 20 | ~0.83 | 10 days | any M chip |
| Pro | `M` | $1,000 | 10 | 80 | ~3.33 | 7 days | Pro or Max |
| Ultra | `L` | $4,000 | 40 | 320 | ~13.33 | 3 days | Ultra |
| Core Latest | `SX` | $375 | 3.75 | 30 | ~1.25 | 10 days | newest base |
| Pro Latest | `MX` | $1,500 | 15 | 120 | ~5 | 7 days | newest Pro or Max |
| Ultra Latest | `LX` | $6,000 | 60 | 480 | ~20 | 3 days | newest Ultra |

- `tier` is a known seat id ("Unknown seat size").
- With `USDC`/`USDT` (or no `pay`), one coin has to cover the whole price ("A Core seat costs … USDC or USDT…").
- With `WALLET`: USDC first, then USDT, then just enough unlocked MLX sold at the spot sell price. The MLX goes to `treasury.mlx` and its dollars straight into the seat. Without `maxMlx`, or if more MLX would be needed: the transaction fails. The record's `sold` shows what was sold.
- The price goes to `treasury.mlx`; the seat gets the next seat number and appears in `account.nodes`.

### transfer_seat {#transfer-seat}

Gives a dedicated seat the signing wallet holds to another wallet. Seats are freely transferable; a transfer is not a sale, so nothing is paid on the ledger and no distributor commission is created or reversed.

| Field | Type | Notes |
|---|---|---|
| `id` | number | The seat number, a positive whole number (the `id` of `/api/node/<id>`). |
| `to` | string | The receiving wallet, for example `luca.mlx`. |

```json title="Body"
{ "type": "transfer_seat", "id": 12, "to": "luca.mlx" }
```

- `id` is a positive whole number ("Seat number must be a positive whole number") and `to` a wallet name ("Send a seat to a wallet name like luca.mlx"); no other fields. **door**
- The seat exists ("There's no seat #12") and the sender holds it ("You don't hold seat #12"). **door**
- `to` isn't the sender ("You can't send a seat to yourself"), exists ("x.mlx does not exist") and is a user wallet ("x.mlx is a protocol account and can't hold a seat"). **door**
- Dedicated seats aren't offered to US persons, so the recipient must have [confirmed it isn't a US person](/network/us-persons#attest). Otherwise `409` "x.mlx can't receive a dedicated seat: only wallets that confirmed they aren't a US person can hold one.". The sender has nothing to confirm. **door**
- The seat leaves the sender's `account.nodes` and joins the recipient's. Its record keeps its number, tier, purchase, payment and lock-in; `owner` becomes the recipient, `buyer` names the wallet that bought (or was granted) it, and `transfers` lists its latest changes of holder (`from`, `to`, `tx`, `height`, `ts`).
- From the next hour the seat mines for the recipient: it is carried by one of the recipient's Macs, and both wallets' lock-in for newly mined MLX follows the seats each now holds (see [Seats](/protocol/seats) and [Lock-ins](/protocol/lock-ins)). MLX already mined and locked stays with the wallet that earned it.
- The effect is `{ "kind": "seat", "from", "to", "node", "tier" }`.

## Devices

### bind_device

Links a Mac (or iPhone/iPad) to the wallet, so its work earns for this wallet. The Mac's own Secure Enclave key signs a link request; the wallet puts that request on the ledger.

| Field | Type | Notes |
|---|---|---|
| `device` | string | The Mac's P-256 public key, compressed, as hex (`02` or `03` then 64 hex characters). |
| `name` | string | The Mac's computer name (control characters removed, trimmed). Private: public reads show the kind of Mac only. |
| `chip` | string | Optional, for example `M4 Pro`. |
| `memoryGB` | integer | Optional. |
| `os` | string | Optional, for example `macOS 26.1`. |
| `ts` | integer | When the Mac signed the request (ms). |
| `proof` | string | The Mac's ECDSA P-256 signature, raw `r‖s` as 128 hex characters. |

- `proof` signs SHA-256 of `canonical({ type: "mlx-device-link", chain, account, device, name, chip, memoryGB, os, ts })` (`linkMessage` in `lib/chain/device.ts`; a missing `chip`/`os` is `""` and a missing `memoryGB` is `0`), where `account` is this wallet's name or `*` for an open request any wallet may take ("The device didn't sign this link request").
- `ts` is close to the transaction's own `ts` ("That link request has expired…").
- A request works once, and must be newer than the Mac's current link ("…was already used", "…older than this Mac's current link").
- A Mac linked to another wallet moves to this one.
- A wallet links up to 32 devices.

How MLX Node builds and shows the request: [Links](/node/links).

### unbind_device

Unlinks a device from the wallet.

| Field | Type | Notes |
|---|---|---|
| `device` | string | The device's public key. |

- The device is linked to this wallet ("That device isn't linked to this wallet").

## System transactions

Signed by the sequencer (`from: "sequencer"`, `nonce: 0`, the sequencer's `pk`). Any other signer fails. Each is idempotent: a second one for the same payment, hour, payout or grant fails ("…already credited", "That hour was already paid").

| Type | What it does | What sends it |
|---|---|---|
| `genesis` | Block 0: the initial allocations. | Once, when the ledger starts. |
| `deposit` | Credits USDC to a wallet against a verified payment (`payment`: invoice, chain, token, amount, signature). One on-chain payment credits once. With `receive: "MLX"` the USDC then buys MLX at the spot buy price in the same transaction, exactly as a `swap` would (a buy under 1 USDC or one the treasury can't fill stays USDC). | The payment watcher after a checkout is paid, and the operator's "credit a transfer" for a payment the watcher missed. |
| `mint_node` | Records a seat paid with crypto, with its payment proof. | The payment watcher after a seat checkout is paid. |
| `grant_node` | Gives a Core seat through a program (`grant.program: "builder"`), once per `grant.ref`. | MLX Builders. |
| `commission` | Pays a distributor's commission as USDC, once per sale (`ref`). | The distributor reconciliation. |
| `compute_reward` | One hour of mined MLX from `compute.mlx`, each payout locked by the lock-in rules and capped per seat unit. Since 29 Oct 2026 a payout can carry `refs`: mining referral bonuses `{to, level, amount}` for up to three inviters, paid from `community.mlx` and locked until the payout's own lock ends ([Mining referrals](/protocol/rewards#mining-referrals)). | The hourly reward run. |
| `withdraw_settle` | Settles a `withdraw`: `paid` burns the escrow (with the payout's transaction hash, `via`, `paidOn`), `rejected` refunds amount and fee. | Automatic payouts (Circle CCTP to Solana) and the operator for payouts by hand. |
| `issue` | Issues USDC to a protocol account against reserves; must name the reserve transfer (`ref`, `chain:signature`). | The operator adding sell-side liquidity. |
| `adjust` | Credits or debits a wallet's USDC with a public reason (`note`); a debit never exceeds what the wallet holds. | The operator correcting a balance. |
| `gift` | Pays a gift from a message the operator sent to a wallet (`id`, `to`, `kind` MLX, USDC or seat, `amount` or `tier`, `note` = the message's title): MLX from community.mlx, USDC from treasury.mlx, or a seat minted like a Builder seat (nothing paid, the curve doesn't move). Once per message and wallet. | The wallet pressing Claim in MLX Node or on wallet.mlx.app, before the message's deadline if it has one. |
| `seat_reward_return` | Returns the up-front reward a seat bought after seats started mining was still granted. | The scheduled ledger job. |
| `relock` | Re-dated every older lock once to the lock-ins version 2 rules (never longer). | Once, when lock-ins version 2 started. |
| `vest` | Turned `team.mlx`'s MLX into 48 monthly locks, once ([Protocol accounts](/protocol/holdings)). | Once, on 27 Oct 2026. |
| `epoch0` | Recorded the wallets that had a linked Mac when invites became required as epoch 0 (no upstream for referral bonuses), once. | Once, on 29 Oct 2026. |
| `reallocate` | Moved the seat reserve (`rewards.mlx`) to `treasury.mlx`, once. | Once, on 29 Oct 2026. |
| `ref_tier` | Sets a wallet's referral tier (`normal`, `kol`, `strategic`), with a public note. | The operator, for a disclosed partnership. |
| `compute_bill` | The Compute API's day: charges API credit, pays the Macs' wallets in USDC, sends the fee to `treasury.mlx`. Running totals, so a repeat moves nothing ([Credit and billing](/compute/credit-and-billing#the-daily-bill)). | Once a day after 00:10 UTC. |

Each system transaction shows on the explorer with its effects and notes, and its balances are in the next [state commitment](/integrity/state).
