Codex · Reference · Smart Contracts
REFERENCE

Smart Contracts

Five Solidity contracts encode the spec's economic rules on-chain. They live in evm/contracts/, compile with solc 0.8.35 (EVM version cancun), and run inside the EVM execution layer. Each maps to a numbered section of the spec.

[SPEC] skeletons — not audited

These are faithful, runnable skeletons that prove the mechanics: ERC-20 cores, fee/relock/extension policies, and hardening are trimmed. They compile cleanly and pass the demos below, but they have not been audited. Treat them as the executable spec, not production code.

The suite

ContractSpecRole
LiquaToken§4/§5the ERC-20 — minted only by chain emission
VeLock§2vote-escrow lock — boost + decaying weight
LiquidityLocker§3proof-of-lock list for any token / LP
Governor98§698%-of-attesting-weight governor
RewardEscrow§5reward-in-instance escrow + gated claim
LiquaPresaleTokensalepLIQUA — transferable presale receipt
LiquaSwapsalepLIQUA → LIQUA 1:1, gated to the ship date
SelfMineLiquidityexchself-mine → zap → time-locked AMM LP

They reference each other in a small graph — the token's only minter is the escrow; the lock is governed by the governor; the governor reads weight from the lock. Because some references are circular, deployment uses predictAddress to pre-compute addresses before wiring.

  RewardEscrow ──mint()──▶ LiquaToken ◀──transferFrom──┐
                                                      │
   only minter                                      VeLock ──veWeight()──▶ Governor98
                                                       ▲ onlyGov            │ CHANGE.call()
                                                       └────────────────────┘

LiquaToken §4 / §5

A minimal ERC-20. The defining property: new supply is minted only by the RewardEscrow as block emission — there is no owner mint and no premine function.

MemberBehaviour
name·symbol·decimals"Liqua" · "LIQUA" · 18 (constants)
rewardEscrowimmutable — the only address allowed to mint
transfer · approve · transferFromstandard ERC-20; transferFrom honours infinite allowance
mint(to, v)require(msg.sender == rewardEscrow) — chain emission only (§5)

Events: Transfer, Approval. Supply rises only at reward claim — see RewardEscrow.

VeLock §2

Vote-escrow LIQUA on a continuous sliding scale, 0 … DMAX (4 years). A longer remaining lock means a higher boost and more weight; both decay linearly with remaining time (veCRV-style), so a fresh 4-year lock starts at the cap and melts toward 1.0× as it approaches expiry.

MemberBehaviour
DMAX4 * 365 days — the sliding-scale maximum (constant)
boostMaxBps5000 = +50% at the cap ("slightly higher") · governed (§6), max 20000
createLock(amount, duration)escrows amount until now + duration (≤ DMAX); one lock per address
increase(addAmount, newDuration)add to and/or extend a lock — never shortens
withdraw()require(block.timestamp >= end) → reverts LOCKED_UNTIL_EXPIRY before expiry
remainingBps(u)remaining-time fraction in bps (0 once expired)
boostBps(u)BPS + boostMaxBps · remaining/BPS1.0× … 1.5×
veWeight(u)amount · remaining/DMAX — the weight Governor98 reads
Hard withdraw gate

withdraw() reverts with LOCKED_UNTIL_EXPIRY until t ≥ end. There is no early-exit path in the contract — this is the §2 no-early-exit rule, enforced in code. Verified on-chain: a 100-LIQUA / 4-year lock reads veWeight 100 and boost 150.00%.

LiquidityLocker §3

Locks any token or LP position for a fixed term and exposes the locks as a public, paginated, chain-verifiable list. The §3 proof-of-lock dashboard renders straight from this — the list is the chain state, not an assertion.

MemberBehaviour
locks (Row[])append-only public list: owner · token · amount · lockedAt · unlockAt · withdrawn
lock(token, amount, unlockAt)escrows until unlockAt (must be future); returns the row id
withdraw(id)owner-only, after the gate — reverts LOCKED before unlockAt
totalLockedOf(token)live locked amount per token
page(start, n) · ownerLocks(o) · count()views for the marketing surface

Events: Locked, Unlocked. The locked-liquidity dashboard is exported from this state by evm/locker-demo.mjs.

Governor98 §6

Two powers — ROLLBACK (R9) and CHANGE (parameter update) — each require 98% agreement. The denominator is attesting ve-weight from a rolling window, supplied by the consensus layer each block (summing the online set on-chain would be unbounded gas), so idle stake can't freeze governance. Full how-to on the Governance page.

MemberBehaviour
QUORUM_BPS9800 = 98% (constant)
attestingWeightthe rolling-window denominator; only the chain sets it via setAttestingWeight (onlyChain)
propose(kind, payload)kind ∈ {ROLLBACK, CHANGE}; payload is abi-encoded (height, or target+calldata)
castVote(id)adds your ve.veWeight(you) once; reverts if you have no weight or already voted
quorumMet(id)forWeight · BPS ≥ QUORUM_BPS · attestingWeight
execute(id)reverts BELOW_98 unless met; CHANGE does target.call(data), ROLLBACK emits for the chain to enact

RewardEscrow §5

The heart of reward-in-instance. A block's emission is not credited as a balance — it is escrowed to the instance address the chain derives from the block's own SOMA fingerprint, and the miner claims it by proving control of that instance (R6 · instance = auth), only after the block has confirmGate confirmations.

// the instance the reward is escrowed to:
rewardSeed = blake3(soma ∥ minerPub ∥ height)        // chain/soma.mjs §5
instance   = address derived from rewardSeed
// emission is minted to that instance ONLY at claim, ONLY after the gate
MemberBehaviour
confirmGate12 — confirmations required before a claim · governed (§6)
headcurrent chain height (the confirmations oracle), set by head_() (onlyChain)
seal(seed, instance, amount, height)at block production — escrow the emission to the derived instance (onlyChain)
claim(seed)the miner's L2 getting-tool — see the gates below

claim() enforces two rules in code:

Only when both pass does it token.mint(instance, amount) — so emission enters supply at claim, by the rightful instance, after finality. Events: Sealed, Claimed.

§5 claim path · wired + verified

The seam around the contract is now built. evm/reward-instance.mjs derives the dual-seed instance — public rewardSeed = blake3(soma ∥ minerPub ∥ height) (the escrow key) and secret instPriv = blake3(minerSecret ∥ rewardSeed), which only the miner can derive. evm/claim-tool.mjs is the miner's L2 getting-tool: it re-derives the instance and calls claim() once the gate clears, with an optional sweep to the miner's wallet (node evm/claim-tool.mjs --miner-priv … --soma … --height … prints the targets).

Verified end-to-end by npm run claim:test (PASS): emission escrowed → claim-before-gate reverts → wrong-secret reverts (R6 · instance = auth) → the instance claims → minted + swept → double-claim reverts, conserved to the wei. The one remaining toggle is making seal() the default emission path in the live miner — today produceBlock credits the coinbase directly so the pool/validator keep native liquidity, and the §5 escrow path runs alongside it.

Presale & swap pLIQUA → LIQUA

Two contracts handle the presale → launch token swap. Contributors receive a transferable receipt now and redeem it 1:1 for LIQUA at the ship date (Aug 1, 2026).

MemberBehaviour
LiquaPresaleToken (pLIQUA)standard ERC-20 minted to contributors by the minter; burnFrom gated to the swap. Transferable, so it can trade pre-launch.
swapOpensAtthe ship date as a unix timestamp — 1785542400 (Aug 1, 2026 UTC)
swap(amount)reverts SWAP_NOT_OPEN before the ship date; otherwise burns amount pLIQUA and transfers amount LIQUA 1:1 from the reserve
reserve()LIQUA held by the swap — funded from the 2% studio allocation; the swap is 1:1 reserve-backed

Each redeem burns the pLIQUA, so total economic supply is conserved across the swap. Verified by npm run swap:test: a pre-ship swap reverts, then after the ship date the buyer redeems 1:1, the pLIQUA is burned, and the reserve drops by exactly the swapped amount.

SelfMineLiquidity exchange

Lives on the orderbook-DEX (orderbook-dex/contracts/), not the evm/ solc suite above — it's the exchange-side locker for self-mining. It takes a miner's emission, zaps it into the LIQUA/mUSDC AMM pool (swap ~half → add liquidity → mint LP), and time-locks the LP on a rolling term. Only the miner withdraws, only after the gate; the emitter can deliver emission and nothing else.

MemberBehaviour
enroll(term) · setTerm · disableminer self-service; MIN_TERM 1d … MAX_TERM 1460d (4y)
accrueFor(miner, amount)emitter-only; pulls LIQUA, zaps it, locks the LP, refreshes unlockAt = now + term (rolling)
withdraw()after disable + term → transfers the InfinityPool LP token to the miner
positionOf · page · previewZapproof-of-lock reads — position, the roll, a zap preview

Single-sided invariant: the LIQUA reserve grows by exactly the mined amount; the quote reserve is borrowed for the swap and returned by the mint (net unchanged). Verified by npx hardhat test in orderbook-dex/ (12 self-mine cases · 68 passing total).

Compile & run

node evm/compile.mjs        # solc 0.8.35 · evmVersion cancun → evm/artifacts/
node evm/suite-demo.mjs     # Token + VeLock: mint → approve → createLock (ve-lock verified)
node evm/gov-escrow-demo.mjs# Governor98 retunes VeLock via 98% CHANGE · RewardEscrow gated→claim
node evm/locker-demo.mjs    # LiquidityLocker → exports the proof-of-lock dashboard
npm run swap:test          # pLIQUA → LIQUA 1:1 · gated to Aug 1 2026 · burn-on-swap · conserved

Artifacts land in evm/artifacts/; evm/abi.mjs is a minimal selector + word codec (no ethers dependency). All contracts compile with zero errors.

LIQUA · CODEX · Smart Contracts · part of Liqua Chain · 7SLF STUDIOS