| title | hashless |
|---|---|
| description | bitcoin's issuance schedule, paced by ethereum blocks and mined with eth |
| status | pre-genesis |
| type | erc-20 (8 decimals), issuance auction, reserve |
| requires | erc-20, eip-2612 |
| contract | not deployed yet |
abstract
hashless is an erc-20 whose supply follows bitcoin's issuance: 50 coins a block, halved every 210,000 blocks, 33 eras, at most 20,999,999.9769 coins. there is no proof of work: a block is mined by paying for it in eth, at a price that rises while mining runs ahead of one block per ethereum block and falls while it runs behind.
most of that eth stays in the contract as the reserve, and the reserve divided by the supply is the floor. any holder can burn coins for the floor at any time, less 1% that stays behind. a tenth of whatever is paid above the floor goes to the builder address. the contract has no owner and can't be changed after deployment.
constants
| name | value | note |
|---|---|---|
| COIN | 108 | 8 decimals, like the original |
| INITIAL_SUBSIDY | 50 coins | per block in era 0 |
| HALVING_INTERVAL | 210,000 blocks | per era |
| ERAS | 33 | era 32 pays 1 base unit a block, then nothing |
| MAX_BLOCKS | 6,930,000 | heights 0 to 6,929,999 |
| max supply | 20,999,999.9769 | 2,099,999,997,690,000 base units |
| HALF_LIFE | 7,200 ethereum blocks | about a day at 12 s |
| CUT_BPS | 1,000 | builder share of the premium |
| BURN_FEE_BPS | 100 | kept by the reserve on every burn |
genesis
the constructor mints block 0, 50 coins, to 0x000000000000000000000000000000000000dEaD. the deployer pays for it, and that payment is the whole first reserve, so it sets the first floor at a fiftieth of it per coin. those 50 coins can never move, which keeps the supply above zero and the floor defined. the hash of the ethereum block before genesis is stored as genesisParent: nothing could have been mined before it.
the deployer may mine a run of blocks right after block 0, in the same transaction, at the price anyone would pay at that moment. at that point the deployer holds nearly the whole supply, so nearly all of what it pays sits in a reserve it mostly owns. at launch those coins go into the uniswap pool. buying from the pool pays whoever holds its positions; mining pays the reserve.
opening
with seed blocks, mining stays closed after the deploy until uniswap v4 holds coins, which the first liquidity in a hashless pool does. until then the clock stands still: the lead, the difficulty and the price of the next block stay where the deploy left them, however long the pool takes. burning and transfers work from the start.
once it does, anyone can call open(), and the first mine() after that point calls it too. opening sets the anchor to the current ethereum block, so the lead carries on from its value at the deploy and counts down one per ethereum block from there. it happens once and can't be undone; later calls do nothing.
before opening, the seed's receiver is the only holder that can move coins (the genesis coins sit at 0x…dEaD), so only it can put coins in a pool. eth on its own doesn't count. the pool manager is set at deploy; on ethereum mainnet the contract refuses any but uniswap v4's (0x000000000004444c5dc75cB358380D2e3dE08A90). a deploy without seed blocks leaves nobody holding coins to pool, so it opens at genesis.
launch pool
the launch puts the seed coins into a uniswap v4 pool, eth / hashless, 1% fee, no hook, as a ladder of one-sided positions: coins only, no eth. the bottom of the ladder is the ask at that moment, or just under it, never under the floor, and the pool opens there. a pool above the ask would only sell coins that mining sells for less. the positions belong to the deployer, so buys from the pool pay the deployer, and the deployer can take the positions out.
one contract, Ladder, opens the pool, adds every rung and opens mining in the transaction that creates it, so the pool never trades without the coins in it.
mining
one hashless block is due per ethereum block. lead is how far mining is ahead of that. the difficulty is the price of a block over its floor value: 2 on schedule, doubled by every day of lead, halved by every day behind, never under 1.
n = blocks next height
s = 5000000000 >> (n / 210000) base units per block
lead = n - (block.number - anchor) once open; before that, n
if lead < -7200: anchor += -7200 - lead, lead = -7200
floor = reserve / supply
D(i) = 2 ^ (1 + (lead + i) / 7200) difficulty of the i-th block in the run
cost(k) = sum over i < k of floor * s * D(i)
= 2 * floor * s * 2^(lead/7200) * (2^(k/7200) - 1) / (2^(1/7200) - 1)
base = ceil(reserve * k * s / supply) the run's value at the floor
cost = max(cost(k), base), rounded up
cut = (cost - base) / 10
reserve += cost - cut
owed += cut
blocks += k
a run of blocks can't cross a halving. whatever eth is sent above the cost comes back in the same transaction. the floor in the formula is read when the transaction starts, so a run is priced at one floor even though its own premium raises the floor as it lands.
burning
paid = floor(reserve * coins * 9900 / (supply * 10000))
reserve -= paid
supply -= coins
the payout for a given number of coins depends only on the floor, which can't fall, so a burn needs no slippage limit beyond the quote.
floor
reserve / supply never decreases.
- mining:
cost - cut ≥ base ≥ reserve * coins / supply, so the eth added per new coin is at least the floor. - burning:
paid ≤ reserve * coins / supply, so the eth removed per coin is at most the floor. the 1% fee raises it. - claiming moves
owed, which was never part of the reserve. - eth sent to the contract directly joins the reserve and is logged as
Donated. eth that arrives without a call (a selfdestruct, a block reward) is moved in by anyone callingsync().
costs round up and payouts round down, so rounding only ever leaves dust in the reserve. the contract's balance is never less than reserve + owed.
schedule
bitcoin aims for a block every 600 seconds. ethereum makes one every 12. at one hashless block per ethereum block an era takes 29.2 days instead of four years, and the whole schedule fits in about 2.6 years instead of 132. halvings come by height, not by date: when mining runs behind, they come later.
| era | heights | subsidy | coins | on schedule | bitcoin |
|---|
rationale
the price of a block is a multiple of the floor, not an amount of eth. a fixed starting price stops meaning anything once the reserve grows, while a multiple of the floor keeps a block tied to what backs each coin at any size.
difficulty stops at 1 because below it a new coin would bring in less eth than the coins already out are backed by, and every holder would lose a little. when mining is more than a day behind, the schedule waits instead of getting cheaper.
the half-life is 7,200 ethereum blocks, about a day. a stalled schedule gets cheap within a day, and mining a day's worth of blocks at once doubles the difficulty (a week's worth multiplies it by 128). the floor those blocks raise comes on top, so the price moves more than the difficulty, most of all while the supply is small.
the builder cut comes only out of the premium, the part of a run's cost above its floor value. a block mined at difficulty 1 has no premium and carries no cut; the blocks after it in the same run cost a little more and do.
on uniswap, hashless trades like any token. while the pool is above the ask, mining and selling into it pays; while it is below the burn value, buying from it and burning pays. either trade pulls the pool back between the two, whenever someone takes it.
security considerations
- immutable. no owner, proxy, pause or upgrade path. a bug can't be fixed.
- not audited. tested with unit, fuzz and invariant tests, plus a run through all 33 eras.
- mining can be front-run like any purchase. send the quote plus a margin; the difference comes back.
- within one ethereum block, one large run costs less than the same blocks split over several transactions, because each premium raises the floor the next run is priced at. across blocks it depends on how far the lead decays in between.
- a large holder can push the schedule ahead by mining and burning in one transaction. it costs them part of the premium and raises the floor for everyone, but it brings the next halving closer and makes mining dearer for others until the lead decays.
- reentrancy is guarded and state is written before eth leaves the contract.
- no oracles. the only outside calls are eth payouts. opening reads the pool manager's coin balance, which is the token's own storage. the reserve is plain eth.
interface
function mine(uint256 lots, address to, uint256 deadline) payable returns (uint256 cost)
function burn(uint256 coins, uint256 minPaid, address to, uint256 deadline) returns (uint256 paid)
function claim() returns (uint256 amount)
function sync() returns (uint256 extra)
function open() returns (bool opened) // once uniswap v4 holds coins; anyone
function quote(uint256 lots) view returns (uint256 cost, uint256 coins)
function quoteBurn(uint256 coins) view returns (uint256 paid)
function floor() view returns (uint256) // wei per coin
function difficulty() view returns (uint256) // wad
function lead() view returns (int256)
function subsidy() view returns (uint256)
function toHalving() view returns (uint256)
function state() view returns (State)
function openedAt() view returns (uint256) // ethereum block mining opened at, 0 before
function poolManager() view returns (address) // uniswap v4's pool manager
event Mined(uint256 indexed height, address indexed miner, address indexed to, uint256 lots,
uint256 coins, uint256 cost, uint256 cut, uint256 reserve, uint256 supply, int256 lead)
event Burned(address indexed from, address indexed to, uint256 coins, uint256 paid,
uint256 reserve, uint256 supply)
event Claimed(address indexed to, uint256 amount)
event Donated(address indexed from, uint256 amount, uint256 reserve, uint256 supply)
event Opened(address indexed pool, uint256 height, int256 lead, uint256 reserve, uint256 supply)
deployment
| chain | ethereum mainnet |
| compiler | solc 0.8.28, optimizer 1,000 runs, evm cancun |
| contract | not deployed yet |