Claim mechanics
The planned Merkle claim, eligibility threshold and seven-day window. Claims are not open.
The distributor holds one Merkle root per hour and a pile of ETH that belongs to the roots still inside their window. Your share of an hour is a leaf in that hour's tree. claim proves the leaf, the contract marks you as paid and sends the ETH, and that is the entire transaction. No registration, no approval, no queue, no lock.
This page describes the planned claim mechanism in MevhoodDistributor.sol and its operator. The token, vault and distributor are not connected. No claim button or wallet lookup is live; the holder plan shows the current launch status.
The root, and why it is funded before you arrive
At the close of the hour the operator builds one tree over (address, amount) pairs. Leaves are keccak256(abi.encode(address, amount)); internal nodes are keccak256 over the sorted pair, which is the format OpenZeppelin's MerkleProof hashes and the format the contract's own _verify expects. One transaction publishes it:
openEpoch(epochId, root, total, snapshotBlock, profit, burned, reserveAfter, holders)
requires epochId == epochCount + 1
requires msg.value == total
Both requirements matter to a holder. The first means epochs cannot be published out of order or twice, so nobody can re-open an hour that already paid. The second means a root is never published without the money behind it: if (msg.value != total) revert ValueMismatch(). By the time a root exists, the ETH is already sitting in the distributor, and it got there in the same transaction that announced the root.
The tree itself is not on chain. Only the 32-byte root is. Your amount and your proof live in a static file the site serves at /proofs/epoch-<id>.json, written by the operator before the publication transaction is sent. That file cannot be rebuilt from the chain by anyone, including us, which is why it is backed up alongside the journal; but it also cannot be forged, because a wrong amount hashes to a leaf that is not under the root and the contract rejects the proof.
The threshold is measured at the snapshot, not when you press
A wallet is a leaf in an hour's tree if it held at least 100,000 $MEVHOOD at that hour's snapshot block, which is latest − 10 at the moment the operator's run started. The operator reads decimals() on the token at the snapshot block and scales the configured whole-token threshold by it, so the number a holder is told and the number the code compares against are the same number.
Three things follow, and all of them bite someone eventually:
- Buying after the snapshot block puts you in the next hour, not this one. Selling after the snapshot block does not cost you this hour, because the tree was already built.
- A wallet under the threshold is not in the tree at all. There is no dust share to collect;
BadProofis what it gets if it tries. - A computed share below
0.00001 ETHis dropped rather than written into a leaf, because a proof costs more gas to spend than the share is worth. The skipped wei stays on the reserve.
Pools, the launchpad, the router, the distributor itself and the reserve wallet are removed before the tree is built. Their balances are not anybody's position.
One claim per wallet per epoch
The contract keeps mapping(uint256 => mapping(address => bool)) claimed and sets your flag before it sends anything. A second attempt on the same epoch reverts with AlreadyClaimed, no matter which wallet software sent it or how fast the two transactions land. Because there is exactly one epoch an hour, "one claim per epoch" and "one claim an hour" are the same rule stated twice.
Epochs are independent. Skipping one does not forfeit the next, and claiming one does not roll the others up. If four hours went by while you were asleep, that is four separate claim calls with four separate proofs, each costing you around 60,000 gas.
Seven days, then the sweep
CLAIM_WINDOW is 7 days, written as a public constant in the contract, which means it is not a policy anyone can change per epoch. The clock starts at closedAt, the block.timestamp of the publication transaction, and the contract compares against it directly:
claim: if (block.timestamp > closedAt + CLAIM_WINDOW) revert WindowClosed();
sweep: if (block.timestamp <= closedAt + CLAIM_WINDOW) revert WindowOpen();
After the window the operator may call sweep(epochId), once, which sends total − claimed back to the reserve and sets swept. One epoch per run, oldest first, so a backlog drains an hour at a time rather than in one large transaction.
Claims expire when block.timestamp > closedAt + CLAIM_WINDOW, whether or not the operator has swept the remainder. Delaying the sweep does not extend the seven-day claim window. The operator cannot sweep early, sweep twice or sweep an epoch that does not exist.
What MEV NOW does, and what it does not
Once launched, a claim interface will call claim(epochId, amount, proof) from the eligible wallet against the distributor. The contract sends the proved amount of ETH. Claims are not available on the current site.
It does not start a trade. It does not tell the bot to do anything. It does not mint, stake, lock, or wrap. It does not ask for a token approval, and the distributor has no code path that could move $MEVHOOD out of any wallet, because the only thing it ever does with the token interface is nothing: token is stored as an immutable address and never called by claim or sweep at all.
The current homepage robot is an artistic visualization. It is not a transaction replay or a signal that a trade or payout has happened. Actual executor transfers are listed separately in the observer.
Every way a claim can revert
These are the contract's own errors, in the order claim checks them. A wallet that hits one has spent gas and moved no money; the state is untouched.
| Revert | What was checked | What it means |
|---|---|---|
NoEpoch | closedAt == 0 | that epoch id has never been published. Either the hour has not closed yet or the id is wrong. |
AlreadySwept | swept is set | the remainder went back to the reserve already. Checked before the window, so a swept epoch reports this rather than WindowClosed. |
WindowClosed | now > closedAt + 7 days | the seven days ran out. The money is still in the contract until somebody sweeps it, but you can no longer take yours. |
AlreadyClaimed | your flag for this epoch | you took this hour already. The flag is per epoch and per address. |
BadProof | the leaf is not under the root | the amount, the address or the epoch does not match the published tree. A stale proofs file and a hand-edited amount both land here. |
ExceedsTotal | claimed + amount > total | a root built over more than the epoch was funded with. This one exists so that a mistake in our tree cannot drain a different epoch's money out of the contract. |
TransferFailed | the ETH send returned false | your address refused the ETH. The whole call reverts, so your claim flag is not set and the share stays in the epoch. |
That last one has an edge worth naming: the contract forwards 30,000 gas to the recipient. An EOA or a plain receive() is fine. A smart-account wallet whose fallback needs more than that will revert on every attempt, and the fix is to claim through a helper contract that can receive cheaply, not to ask us for anything.
Checking a proof before you spend gas
Nothing here requires trusting the file the site serves. Pull /proofs/epoch-<id>.json, take the amount and proof for your address, hash keccak256(abi.encode(address, amount)), fold the proof over it with sorted pairs, and compare the result against the root field of that epoch's Epoch log on Blockscout. If the two agree, the contract will accept the claim, because that fold is exactly what _verify does. If they disagree, the file is wrong and the transaction would have reverted with BadProof anyway.
After the distributor is deployed, its explorer page can expose epochs(id) for total, claimed, closedAt and swept, and claimed(id, address) for a wallet's claim status. The current homepage does not provide a wallet lookup.