Docs
PRICE
/ ROME
UPEGS
0
CURRENT SUPPLY
0 ROME
MAX SUPPLY
0 ROME

IMPERIAL CODEX

— LEX SCRIPTA —

ROMAN LEGION is an on-chain game running on a single Uniswap v4 pool. Every 1 ROME token held is paired 1:1 with a Legion Upeg: buying ROME mints them, selling burns them. Stake your Legion to earn a share of the daily emission, and every buy enters a lottery for the Spoils Of War pot.

Network · EthereumAsset · ROME (ERC-20)Upeg · on-chain SVGDEX · Uniswap v4 (canonical pool + hook)

THE GAME LOOP

  1. I

    ACQUIRE

    Swap ETH for ROME via the canonical Uniswap v4 pool. Each whole unit of ROME you receive mints a new deferred Legion Upeg into your wallet (seed = 1, class = pending). A flat 1% buy tax is burned on the spot.

  2. II

    REVEAL

    Two blocks after the buy, the Legion's class and mining power are deterministically derived from a commit-reveal entropy mix (see Commit-Reveal below). You can force it with revealUpeg(id), or just wait — every swap on the pool auto-anchors up to 5 from the global queue.

  3. III

    ENLIST

    Stake your anchored Legion (up to 32 per tx) to claim a share of the daily emission proportional to your total mining power. The pot is 80% of emissions — the other 20% feeds the Spoils Of War lottery.

  4. IV

    CLAIM

    Withdraw your accrued wages whenever you want. A 10% claim tax is split back across every other staker as a “refined-ore boost” — the longer you stay staked, the more it compounds. Unstaking auto-claims.

  5. V

    CONQUER

    Every buy ≥ 0.01 ETH rolls for the Spoils Of War pot. Odds scale linearly from 0.016% at the floor up to a capped 1.00% at 1 ETH. A win is split 50/50 between the buyer and a power-weighted random staker.

TOKEN & EMISSIONS

Initial supply10,000 ROMEPremined to the hook for liquidity bootstrapping.
Day-1 emission1,000 ROME / 24hStreamed per-second to the staker pool.
Daily decay× 0.99Each day's emission is 99% of the previous.
Half-life~69 daysDaily emission halves every ~69 days. Decay is geometric, never zero.
Emission split80 · 2080% to stakers · 20% to Spoils Of War pot.
Buy tax1%Taken on ROME out and immediately burned.
Sell tax1%Taken on ETH out, funds the buyback bucket.
Claim tax10%Redistributed to other stakers via the refined-ore boost.
Per-tx buy capmaxBuy()Starts at 2.5% of supply, +3bps every second after launch.
Constants & formulas
UNIT_PER_UPEG       = 1e18            // Rome.sol:21
INITIAL_SUPPLY      = 10_000e18       // Rome.sol:22
DAY_1_EMISSION      = 1_000e18        // Rome.sol:25
DAILY_DECAY_WAD     = 0.99e18         // Rome.sol:26
SPOILS_OF_WAR_PCT   = 20              // Rome.sol:114
TAX_BPS (hook)      = 100  (= 1%)     // RomeHook.sol:49

currentEmissionPerSec
  = DAY_1_EMISSION * DAILY_DECAY_WAD^d / WAD / 86_400
    where d = (now - launchTimestamp) / 86_400

THE FIVE CLASSES

Every revealed Legion is assigned a class (1 of 5) and a hash value in [1, 100]. Mining power is the product. Higher-class Legion are rarer but multiply your stake share dramatically.

RankClassOdds×Mining power
ITiro60%×11 – 100
IIMiles25%×22 – 200
IIICenturio10%×33 – 300
IVTribunus4%×44 – 400
VLegatus1%×55 – 500
Derivation
class:  r = keccak256(upegId, seed, "ROME_CLASS") % 10_000
        Tiro      [    0,  5_999]
        Miles     [6_000,  8_499]
        Centurio  [8_500,  9_499]
        Tribunus  [9_500,  9_899]
        Legatus   [9_900,  9_999]

hash:   h = (keccak256(upegId, seed, "ROME_HASH") % 100) + 1
power:  miningPower = h × multiplierOf(class)

All artwork (helmet, armor, weapon, face, beard, earring, …) is rendered 100% on-chain as composed SVG. No IPFS, no off-chain dependency.

COMMIT-REVEAL — ANTI-SNIPE

A Legion's class isn't known at mint time — it's sealed by a two-block commitment that no single proposer can grind. Until revealed, every new Upeg shows seed = 1 (the deferred sentinel) and a power of zero.

  1. 1

    Mint (block N)

    Buy mints the Upeg with seed = 1 and records mintBlockOf[id] = N.

  2. 2

    Wait (≥ 2 blocks)

    Two block hashes (N+1 and N+2) need to exist before the reveal can proceed. This binds the outcome to a multi-block commitment.

  3. 3

    Reveal

    Entropy = keccak256(blockhash(N+1), blockhash(N+2), hookSeed, "ROME_ANCHOR"). Class and hash are derived from this seed (see Classes). If 256 blocks have passed since mint and the hashes are pruned, a deterministic fallback fires using the latest blockhash.

revealUpeg(id)

Force-reveal a single Upeg. ~100k gas. Anyone can call. Use it the moment you want your Legion active.

revealUpegsBatch(ids[])

Reveal many in one tx; cheaper per-Upeg because the hook's random seed is read once.

anchorPending(N)

Walks the global queue from the front, anchoring up to N ≤ 20 deferred Upegs per call. Cheap way to clear the backlog for everyone.

Auto-sweep

Every swap on the canonical pool auto-anchors up to 5 Upegs and auto-resolves up to 2 Spoils rolls — so high pool activity keeps the queue short for free.

STAKING & REFINED-ORE BOOST

Stake any number of revealed Legion (up to 32 per transaction). Your share of the staker pool is your total mining power divided by global staked power, paid per second.

Refined-ore boost

When other stakers claim, 10% of their pending ROME is taxed at the contract level. That tax is not burned — it's redistributed pro-rata to every active staker (including the one claiming) by scaling the accRomePerPower accumulator. Effectively: the longer you stay staked, the more of other people's claim taxes compound into your share.

  • Unstake auto-claims. Withdrawing a Legion settles its accrued ROME first, taking the 10% tax.
  • Only anchored Upegs stake. A deferred Upeg (seed = 1) has zero power until revealed.
  • Selling burns from the held set first. Staked Legion are safe from auto-burn on a sell — the lowest-id held Upegs get burned to rebalance your ROME count.
  • Auto-service. Every stake/unstake/claim opportunistically anchors up to 3 Upegs from the global queue and resolves up to 2 Spoils rolls — paying that cost back to the community while you transact.
Reward math
pending(user)
  = userTotalPower × (accRomePerPower − userRewardDebt) / WAD

On every settlement:
  emission        = secondsSinceLast × currentEmissionPerSec
  spoilsAdd       = emission × 20 / 100
  stakerEmission  = emission − spoilsAdd
  accRomePerPower += stakerEmission × WAD / totalStakedPower

On claim:
  payout = pending × 90 / 100
  tax    = pending − payout      // distributed to other stakers
                                    via taxScalar / accRomePerPower bump

SPOILS OF WAR

A jackpot that accumulates 20% of every emission tick and is rolled for by every qualifying buy. Half goes to the buyer who wins, half goes to a random staker weighted by power.

When does a buy roll?

  • Buy must include ≥ 0.01 ETH of ETH value.
  • You're cooldown-locked for 5 blocks after a roll (anti-spam).
  • The roll resolves once two later blockhashes are available — same commit-reveal trick as Upeg minting.

Odds table

ETH paidHit probability
0.010.0160%
0.100.1054%
0.500.5030%
1.001.0000% (cap)
> 1.001.0000% (capped)

If you win

The pot is split 50/50:

  • Your half is minted directly to your wallet.
  • The other half goes to a staker chosen at random with probability proportional to their staked power (Fenwick tree). If there are no eligible stakers, that half returns to the pot for the next roll.

Anyone can call resolveSpoilsOfWar(maxToResolve) (~800k gas hint) to clear pending rolls if the auto-sweep is lagging.

Roll formula
threshold = min(
  ROLL_HIT_INTERCEPT + (ethPaid × ROLL_HIT_SLOPE) / 1 ETH,
  ROLL_HIT_CAP
)
hit ⇔ keccak256(blockhash(N+1), blockhash(N+2),
                buyer, poolSnapshot) % ROLL_DENOM  <  threshold

ROLL_DENOM         = 1_000_000     // Rome.sol:108
ROLL_HIT_INTERCEPT = 60            // Rome.sol:109
ROLL_HIT_SLOPE     = 9_940         // Rome.sol:110
ROLL_HIT_CAP       = 10_000        // Rome.sol:111  (= 1.0%)

BUYBACK ENGINE

The 1% sell tax on every ROME → ETH swap accumulates as ETH in the hook's buyback bucket. When it crosses a configurable threshold, anyone can trigger a buyback and the protocol uses the ETH to buy ROME back on the same pool — every token bought is then burned.

  • Default threshold: 0.01 ETH (range 0.01–10 ETH, adjustable by the owner).
  • Call: buybackIfReady(uint256 minRomeOut) — anyone, with their own slippage protection.
  • Owner can pause buybacks via setBuybackPaused(true) as a kill-switch.
  • Every buyback is logged on-chain — the dashboard's Buybacks panel reads from buybackAt(i).

SAFETY & ADMIN

  • No pause on ROME transfers or staking. The token and staking are non-custodial and always live.
  • Owner-only knobs are minimal: set the hook once, point the canonical pool once, adjust buyback threshold, pause buybacks, sweep stranded non-bucket ETH from the hook. None of these can drain user balances or staked Legion.
  • Upeg art is 100% on-chain. No IPFS dependency, no gateway can break the metadata.
  • Quoter is display-only. It returns a first-order spot quote — never use it for slippage protection. The trade panel runs a depth-aware simulation via eth_simulateV1 before binding minOut.

DEVELOPER REFERENCE

— FOR INTEGRATORS —

Below is a non-exhaustive cheat-sheet of public functions on each contract. ABIs ship in @rome/abis and are bundled with the front-end — read them straight from there for an integration-ready source of truth.

Rome.sol — token, staking, claims
// Token
function balanceOf(address) view returns (uint256)
function transfer(address to, uint256 amount) returns (bool)
function approve(address spender, uint256 amount) returns (bool)

// Staking
function stakeForMining(uint256 upegId) external
function stakeForMiningBatch(uint256[] upegIds) external
function unstakeFromMining(uint256 upegId) external
function unstakeFromMiningBatch(uint256[] upegIds) external

// Claim & rewards
function claim() external
function pendingRewards(address user) view returns (uint256)

// Reveal & spoils
function revealUpeg(uint256 upegId) external
function revealUpegsBatch(uint256[] upegIds) external
function anchorPending(uint8 maxToAnchor) external
function resolveSpoilsOfWar(uint8 maxToResolve) external

// Reads
function getClass(uint256 upegId) view returns (uint8)
function getHash(uint256 upegId) view returns (uint8)
function getMiningPower(uint256 upegId) view returns (uint16)
function currentEmissionPerSec() view returns (uint256)
function maxBuy() view returns (uint256)
RomeHook.sol — v4 hook, buybacks
function buybackIfReady(uint256 minRomeOut) external
function randomSeed() view returns (uint256)
function qualifyingBuyMin() view returns (uint256)

// owner-only
function setCanonicalPool(PoolKey key) external onlyOwner
function setBuybackThreshold(uint128) external onlyOwner
function setBuybackPaused(bool) external onlyOwner
RomeSwapRouter.sol — buy / sell entry
function buy(uint256 minRomeOut) external payable returns (uint256)
function sell(uint256 romeIn, uint256 minEthOut) external returns (uint256)
RomeQuoter.sol — display-only price
function currentSqrtPriceX96() view returns (uint160)
function currentPrice() view returns (uint256)
function quoteBuy(uint256 ethIn) view returns (uint256)   // first-order
function quoteSell(uint256 romeIn) view returns (uint256) // first-order
RomeLens.sol — read aggregator
function getGlobalStats() view returns (GlobalStats)
function getUserStats(address) view returns (UserStats)
function getOwnedUpegs(address, page, pageSize)  view returns (UpegInfo[])
function getStakedUpegs(address, page, pageSize) view returns (UpegInfo[])
function getStakerPage(page, pageSize) view returns (StakerInfo[])
function getRecentSpoilsOfWarHits(limit) view returns (SpoilsOfWarHitRecord[])
function getRecentBuybacks(limit) view returns (BuybackRecord[])

Contract addresses are listed on the dashboard.

SENATVS · POPVLVSQVE · ROMANVS
ROMAN LEGION