# Lock-ins

Mined MLX is locked from the UTC day it was earned, for the lock-in of the wallet's shortest-locked seat, or {{freeLockDays}} days on a free seat. One lock per day, released on its date. USDC and spot-bought MLX are never locked.

## The rule

Each seat has a lock-in in days (`lockDays` in `TIERS`, `lib/chain/curve.ts`). The smaller the seat, the longer the lock. The values are in the "Mined MLX unlocks after" column:

| 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 |

When an hour is paid, the ledger works out each wallet's lock from the seats it holds at that moment (`computeLockDays`):

- with one or more dedicated seats: the **shortest** `lockDays` among them;
- with none: `COMPUTE_FREE_LOCK_DAYS`, 30 days.

The unlock time is counted from the start of the UTC day the hour belongs to (`computeUnlockAt`):

```text
day   = the hour's start, rounded down to 00:00 UTC
until = max(day, MAINNET_LAUNCH) + lockDays × 24 h
```

So MLX mined in any hour of a day unlocks at the same moment. MLX mined before mainnet counts from mainnet.

## One lock per day

A wallet's mined MLX is one `compute` lock per UTC day (`Lock.kind: "compute"`, with `from` set to the day). Each hour's payout is added to its day's lock. If a later hour of the same day comes with a shorter lock-in (the wallet bought a seat that day), the whole day takes the shorter date. A lock never gets longer.

```text
{ kind: "compute", amount: <base units>, from: <the day, ms>, until: <unlock time, ms>, node: 0 }
```

## Release

A lock past its `until` is plain MLX. The ledger folds every compute lock that has passed its date into the wallet's MLX balance whenever the account is next written (`matureCompute` in `lib/chain/ledger.ts`). It does the same for every kind of lock before a transfer, a spot trade or a seat paid from the wallet (`settleLocks`). Nothing has to be claimed.

Until then, locked MLX can't be sent, sold, or used to pay for a seat. Transfers and sales take only the unlocked balance.

## Other locks

| `kind` | What it holds | Lock-in |
| --- | --- | --- |
| `compute` | mined MLX | as above |
| `node` | a seat's up-front reward, for seats bought before `SEAT_REWARD_END_AT` | the seat's `lockDays` from purchase (`unlockAt`) |
| `referral` | the referrer's share of that reward | the same date as its seat |
| `vest` | `team.mlx`'s MLX, in monthly tranches (the `vest` system transaction) | each tranche's own date |
| `swap` | legacy | its own date |

Seats bought since `SEAT_REWARD_END_AT` were paid nothing up front, so they hold no `node` lock. Their seat record still carries an `unlockAt` date.

## Version history

Lock rules are versioned by transaction time, so the chain replays exactly.

**Version 1.** From the start of compute rewards, mined MLX was locked for `COMPUTE_LOCK_MONTHS_V1` months from the day it was earned. Seat rewards were locked for their tier's `lockMonths` from purchase.

**Version 2,** from `LOCKS_V2_AT`. The day rules above. Transactions before that time keep the month rules when they replay.

**The `relock` transaction.** Once `LOCKS_V2_AT` had passed, the produce cron sent one `relock` system transaction (`relockOnce` in `lib/chain/ledger.ts`, guarded by `relock:v2` in the ledger store). It re-dated every lock made under the month rules:

- `node` and `referral` locks: to the earlier of their old date and the seat's day-rule date;
- `compute` locks still on the month rule: the day they were earned is recovered from the old date, then the lock takes the earlier of the old date and the day-rule date for the wallet's current seats;
- seat records: their `unlockAt` the same way.

Nothing was locked longer than before. A second `relock` fails with "Already re-dated".

Lock-ins are protocol parameters. They may be made longer for **new** locks later, with a new version cutover. Existing locks keep their dates.
