Whitepaper
A fully on-chain game running entirely on a Uniswap v4 hook.
Abstract
Mine pixels. Earn ETH. Hash-based placement. Proximity scoring. Accumulator yield.
Genesis is a fully on-chain game where every $GENESIS token is a mineable pixel on a 100x100 grid. The entire game — token minting on buy, pixel placement, strike resolution, scoring, prize pool distribution, and supply burns — lives inside a single Uniswap v4 hook contract. There is no separate game contract. There is no off-chain coordination. There are no oracles or middleware. The hook reacts to every swap, mints or burns pixels accordingly, and runs the strike-based scoring game in lockstep with the trading layer.
This is the first project to use a Uniswap v4 hook as the entire substrate for an on-chain game.
What Genesis Is
Genesis is a game disguised as a token. Or a token disguised as a game — the distinction is meaningless because they're the same contract.
Every $GENESIS token corresponds to one cell on a 100x100 grid. There are exactly 10,000 cells and 10,000 tokens. When you buy $GENESIS on Uniswap, the hook prepares pixels for you. When you visit the /claim page and call claimPixels(), those pixels get placed on the grid at deterministic, hash-derived coordinates. When you sell, your pixels burn off the grid in reverse order. When the contract fires a "strike" at a coordinate, nearby pixels score points. At the end of each 6-hour round, the prize pool — funded by a 3% tax on every swap — distributes to holders proportional to how much their pixels scored.
Pixels can be permanently sealed in exchange for a one-time payout based on lifetime score. Sealing burns the underlying token, reduces total supply, and converts the pixel from green to white on the grid forever.
The game has no admin, no team allocation, no upgrade path, and no off-chain components. It runs entirely as a Uniswap v4 hook on Ethereum.
The Grid
The grid is a 100x100 lattice — 10,000 cells indexed by (x, y) coordinates from (0,0) to (99,99). Each cell is in one of three states:
| Empty | No pixel placed here |
| Live | A pixel is placed, owned by a wallet, eligible to score |
| Sealed | Permanently locked. Cannot be removed, cannot score |
The grid lives entirely on-chain. Occupancy is stored as a packed bitmap (10,000 bits across 313 storage slots). Per-pixel state — owner, current round score, lifetime score, sealed status — is stored in mappings keyed by coordinate. The frontend reads chain state and renders client-side. No IPFS, no metadata server, no centralized rendering service.
Buying and Claiming Pixels
BUYING
Buying $GENESIS is a normal Uniswap swap. You send ETH to the pool, the pool returns $GENESIS tokens, and the hook charges a 3% tax routed to the prize pool and seal reserve.
Buying does NOT immediately place pixels on the grid.It gives you tokens tracked as "unclaimed pixels" — they belong to you, but have no coordinate, no score, and no participation in the game.
CLAIMING
To place tokens onto the grid as pixels, call claimPixels() on the hook contract. When you do:
| 1 | Hook reads how many tokens haven't been placed yet |
| 2 | For each unclaimed token, derives a coordinate via keccak256(your_address, your_pixel_ordinal) |
| 3 | If occupied, re-hashes with incrementing salt until empty cell found |
| 4 | Pixel placed at coordinate, ownership recorded |
| 5 | Unclaimed count decremented |
keccak256(addr, ordinal)
● (44,91) · ● (62,12)
● (78,55)
WHY SEPARATE CLAIM FROM BUY
Auto-placing on every buy would inflate gas costs dramatically. A 50-token purchase would face 50 placement operations in a single swap. By separating: swaps stay cheap, claim cost is paid only when the holder wants to enter the game, and holders can claim batches paying gas once for many pixels.
Pixels that aren't claimed don't score, don't earn, and don't appear on the grid. The frontend shows your unclaimed count and prompts you to claim whenever it's greater than zero.
Pixel Placement
Placement is deterministic, hash-derived, and unpredictable in practice. Holders cannot choose coordinates.
coord = keccak256(wallet_address, pixel_ordinal) mod 10000
COLLISION RESOLUTION
If derived coordinate is occupied, the hook re-hashes with an incrementing salt:
uint256 salt = 0;
while (salt < 256) {
uint16 coord = uint16(
uint256(keccak256(abi.encode(wallet, ordinal, salt))) % 10000
);
if (!_isOccupied(coord)) return coord;
salt++;
}Salt loop bounded at 256 iterations. On a half-full grid the expected iterations to find an empty cell is 2. On a 90%-full grid, it's 10. The bound is a safety rail; in practice it's never approached.
WHAT THIS MEANS FOR THE GAME
| Unpredictable | Cannot pre-compute coordinates without knowing exact ordinal at claim time |
| Fair | Whales cannot stack pixels in a chosen region — they scatter via hash output |
| A roll | Every claim reveals landing positions only after the transaction confirms |
| Valuable luck | Landing adjacent to dense regions is mathematically more valuable than landing alone |
Selling
Selling $GENESIS reverses placement. When tokens leave your wallet via swap:
| 1 | Hook detects your balance decreased by N tokens |
| 2 | Hook removes N of your most recently claimed (non-sealed) pixels |
| 3 | Removal is LIFO — last placed, first burned |
| 4 | Sealed pixels are skipped in LIFO traversal — they persist forever |
Your pixels: [P1, P2, P3, P4, P5] Sell 2 tokens → removes P5, then P4 Remaining: [P1, P2, P3] If P3 was sealed → skips P3, removes P5 then P4 anyway
The "burn" here is grid-level, not token-level — tokens go back to the LP unchanged. Total token supply is unaffected by sells. Only sealing reduces total supply.
Rounds and the Game Cycle
Genesis runs in 6-hour rounds. Each round is a self-contained scoring cycle. Every swap routes 80% of the 3% hook tax into the round's prize pool. Six strikes fire over six hours, scoring affected pixels.
Strikes
A strike is the core scoring event. Once per hour during a round, the contract fires a strike at a deterministic coordinate derived from recent block state and a strike nonce:
strikeCoord = keccak256(blockhash(block.number - 1), strikeNonce++) mod 10000
Strikes are deterministic given inputs but unpredictable in practice — nobody can predict future block hashes. When a strike lands, it affects a 3x3 area centered on that coordinate:
For each cell in the 3x3 area: if empty, no score. If occupied by a live pixel, score is added. If occupied by a sealed pixel, skipped. A single strike can score up to 9 different pixels owned by up to 9 different wallets.
EDGE HANDLING
If the strike center is at a grid edge, some neighbor positions don't exist and aren't scored. No wraparound — the grid has hard edges.
STRIKE TRIGGERING
Strikes are fired by the platform wallet calling tick(). The function enforces a minimum 1-hour gap. If called too early, it reverts. The platform wallet cannot fire extra strikes, skip strikes, or alter the deterministic coordinate calculation.
Proximity Scoring
This is the mechanic that makes Genesis a game rather than a lottery. Your pixel scores when a strike directly hits it (+5), AND when a strike hits any of its 8 neighbors (+1). The more pixels in your local area — yours or anyone else's — the more chances any single strike scores you.
A pixel in an empty region scores only when a strike lands directly on it. A pixel surrounded by other pixels scores from strikes that hit it AND from strikes hitting any of its 8 neighbors. Other holders' pixels nearby benefit you, simply by existing.
MATHEMATICAL EXPECTATION
For a uniformly random strike on a grid with occupancy ratio p:
E[score per strike] = (1/10000) × 5
+ (8p × 1/10000) × 1
= (5 + 8p) / 10000
Empty region (p ≈ 0): 5 / 10000 per strike
Fully occupied (p = 1): 13 / 10000 per strike (+160%)WHY THIS CHANGES THE GAME
| Adjacency value | Landing next to existing pixels is more valuable than landing in empty space |
| Network effect | Existing holders benefit when new buyers land nearby — more scoring surface |
| Sell penalty | Selling weakens local scoring for surviving neighbors |
| Whale resistance | Capital alone does not create zone control — scatter is hash-determined |
The Bounty Pixel
Each round, the hook designates a single coordinate as the bounty pixel:
bountyCoord = keccak256(blockhash, roundNumber) mod 10000
If a strike's center lands on the bounty coordinate AND a pixel exists there, that center pixel's score is multiplied by the current bounty multiplier. The multiplier starts at 1x, increments by 1 each round a strike misses it, capped at 5x. When hit, resets to 1x.
You can't choose to own the bounty pixel — coordinates are randomized each round and your placement is hash-determined. Owning the bounty in any given round is a function of how many pixels you have and pure luck.
Accumulator Yield
Genesis distributes earnings using a MasterChef-style accumulator pattern — the same math that powers SushiSwap and Curve gauge systems.
THE PROBLEM
Distributing prize pool proportionally would require iterating every scoring pixel at round close. With thousands of scoring pixels, this would exceed block gas limits.
THE SOLUTION
A single global counter — payoutPerScoreAccumulator — increases monotonically at every round close:
payoutPerScoreAccumulator += prizePool / totalScore
Each pixel records the accumulator value at its last settle point. Claimable yield is computed lazily:
claimable = pixel.unsettledScore
× (currentAccumulator - pixel.lastSettledAccumulator)This is O(1) per pixel. No iteration over the holder set. Distribution happens automatically as the accumulator advances; claiming materializes the math when the holder calls claim().
LAZY SETTLEMENT IN PRACTICE
Round 47 closes: accumulator = 0.0001234
pixel scored 7 pts, holder hasn't claimed.
Round 48 closes: accumulator = 0.0001891
pixel scored 0 pts.
Round 49 closes: accumulator = 0.0002445
pixel scored 3 pts.
Round 50: holder calls claim()
→ 7 pts × R47 delta + 3 pts × R49 delta
→ computed in O(1) reads
→ holder receives ETH| No iteration | Scales to any holder count with no gas growth |
| Holder pays gas | Claim cost is the holder's responsibility |
| Seal compatible | Sealed pixels stop accumulating without breaking the math |
| Provably proportional | Distribution is always correct — the math cannot be gamed |
Sealing: Deflationary Endgame
Sealing is the deflationary endgame mechanic. Holders can permanently lock a pixel for a one-time payout based on lifetime score.
WHAT SEALING DOES
| 1 | Verifies caller owns the pixel |
| 2 | Verifies at least 100 lifetime score (anti-griefing minimum) |
| 3 | Settles the pixel — materializes pending yield |
| 4 | Transfers lifetimeScore × sealRate ETH from seal reserve |
| 5 | Transfers any pending claimable yield |
| 6 | Marks pixel as sealed — turns white, excluded from all future strikes |
| 7 | Burns 1 GENESIS token from caller balance |
| 8 | Removes pixel from LIFO ownership array (immune to future sells) |
WHERE PAYOUTS COME FROM
Sealing is funded by a dedicated seal reserve, NOT the round prize pool. The seal reserve receives 20% of every hook tax. Sealing doesn't drain active round payouts. If temporarily insufficient, the call reverts and the holder waits.
THE DEFLATIONARY EFFECT
Every seal reduces total $GENESIS supply by 1. As more pixels seal, fewer remain to score, but the prize pool keeps growing (funded by trading volume, not pixel count). Per-pixel yield rises for remaining live pixels. In the limit, one pixel surviving would score nearly every strike and earn nearly the entire prize pool every round.
The Hook Tax
Every swap on the $GENESIS pool is taxed 3% by the hook, in addition to the standard 0.30% Uniswap LP fee.
| 0.30% LP fee | Accumulates to the LP position. LP NFT burned at launch — fee stays permanently locked. |
| 2.40% to prize pool | 80% of hook tax. Funds round payouts to scoring pixels. |
| 0.60% to seal reserve | 20% of hook tax. Funds seal payouts to holders who permanently lock pixels. |
| Total swap cost | ~3.30% per trade, symmetric on buys and sells. |
Coupling rewards to the LP fee tier would force a tradeoff: high LP fees fund the game but discourage trading. By using a dedicated hook tax, the LP fee stays trader-friendly, the game is funded generously, and the seal reserve grows independently.
Why a v4 Hook
Genesis is the first project to run a complete on-chain game inside a single Uniswap v4 hook. Everything described in this document happens inside one contract that hooks into the pool's swap lifecycle.
WHAT V4 HOOKS UNLOCKED
Uniswap v4 introduced programmable execution at every pool lifecycle event. For Genesis, the critical hook is afterSwap. Every swap atomically triggers:
| 1 | Hook tax calculation and routing (80% prize pool / 20% seal reserve) |
| 2 | Detection of buy vs sell direction |
| 3 | On buys: increment buyer unclaimed pixel count (placement deferred) |
| 4 | On sells: LIFO burn of seller claimed pixels from grid |
All of this happens in the same transaction as the swap itself. There is no off-chain bridge, no oracle, no relayer between the trade and the game state.
PLATFORM WALLET CAPABILITIES
STORAGE ARCHITECTURE
| gridBitmap[313] | Packed occupancy bitmap (10,000 bits across 313 uint256 slots) |
| pixels[coord] | Owner, scores, seal status per coordinate |
| pixelsOf[wallet] | Pixel array per wallet — enables LIFO burn on sell |
| unclaimedPixels[wallet] | Pending unclaimed pixel count per wallet |
| payoutPerScoreAccumulator | Global monotonic accumulator (1e18 scaled) |
| roundClosedAccumulator[r] | Accumulator snapshot at each round close |
| sealReserve | Current sealing reserve balance |
| currentBountyCoord | Active bounty coordinate for current round |
| bountyRolloverMultiplier | Current bounty multiplier (1-5) |
WHY NOT BEFORE V4
| v2/v3 | No hook system. Extra mechanics required separate contracts or token-level modifications that broke composability. |
| Other AMMs | Balancer and others have hook-like systems but lack v4's depth of swap lifecycle integration. |
| Custom pools | Deploying a new AMM sacrifices Uniswap's existing liquidity layer and routing infrastructure. |
Risk Disclosures
| Smart contract risk | The hook is immutable post-launch. Bugs cannot be patched. Audits reduce but do not eliminate risk. |
| Game-theoretic risk | Novel mechanics may produce unexpected emergent behaviors. Strategies that work early may not work late. |
| Hook tax risk | The 3% tax creates meaningful per-swap friction. Total cost (LP fee + hook tax) is ~3.3%. |
| Sealing risk | Sealing is permanent and irreversible. The token is burned and cannot be recovered. The seal reserve may temporarily lack funds. |
| Claim risk | Pixels that aren't claimed don't score and don't earn. Failing to claim is a self-imposed loss. |
| Platform wallet risk | Required to advance game state. If unavailable, future rounds stall — though existing claims, sealing, and trading continue. |
| Liquidity risk | Initial LP is locked. Market volatility and large sells can produce severe slippage. |
| Regulatory risk | Token mechanics involving rewards may be subject to varying legal interpretations. Not legal advice. |
Appendix: Core Functions
A. STRIKE RESOLUTION
function _resolveStrike(uint16 centerCoord) internal {
uint16 cx = centerCoord % 100;
uint16 cy = centerCoord / 100;
for (int8 dx = -1; dx <= 1; dx++) {
for (int8 dy = -1; dy <= 1; dy++) {
int16 nx = int16(cx) + dx;
int16 ny = int16(cy) + dy;
if (nx < 0 || nx >= 100 || ny < 0 || ny >= 100) continue;
uint16 coord = uint16(uint16(nx) + uint16(ny) * 100);
if (!_isOccupied(coord)) continue;
if (pixels[coord].sealed) continue;
_settlePixel(coord);
uint64 points = (dx == 0 && dy == 0) ? 5 : 1;
if (dx == 0 && dy == 0 && coord == currentBountyCoord) {
points *= bountyRolloverMultiplier;
bountyHitThisRound = true;
}
pixels[coord].currentRoundScore += points;
currentRoundTotalScore += points;
}
}
}B. CLAIM PIXELS
function claimPixels() external {
uint256 toPlace = unclaimedPixels[msg.sender];
require(toPlace > 0, "no unclaimed pixels");
for (uint256 i = 0; i < toPlace; i++) {
uint16 coord = _derivePixelCoord(
msg.sender,
walletPixelOrdinal[msg.sender]
);
_placePixel(coord, msg.sender);
walletPixelOrdinal[msg.sender]++;
}
unclaimedPixels[msg.sender] = 0;
emit PixelsClaimed(msg.sender, toPlace);
}C. SEALING
function seal(uint16 coord) external {
require(pixels[coord].owner == msg.sender, "not owner");
require(!pixels[coord].sealed, "already sealed");
_settlePixel(coord);
require(pixels[coord].lifetimeScore >= 100, "insufficient score");
uint256 sealPayout = pixels[coord].lifetimeScore * sealRate;
uint256 totalPayout = sealPayout + pixels[coord].claimableYield;
require(sealReserve >= sealPayout, "seal reserve insufficient");
sealReserve -= uint128(sealPayout);
pixels[coord].claimableYield = 0;
pixels[coord].sealed = true;
_burnToken(msg.sender, 1e18);
_removeFromOwnershipArray(msg.sender, coord);
payable(msg.sender).transfer(totalPayout);
emit Sealed(coord, msg.sender, sealPayout, pixels[coord].lifetimeScore);
}Genesis is a game where every $GENESIS token is a coordinate, every claim is a roll, every six hours is a payout, and every seal is a permanent etching on the canvas. The entire mechanic lives inside a single Uniswap v4 hook contract that runs the trading layer, placement layer, scoring layer, distribution layer, and deflationary layer simultaneously.
This is the first time a complete on-chain game has been built this way — entirely as a v4 hook, with no separate game contract, no off-chain coordination, and no middleware between the swap and the gameplay.