Love
The one and only LOVE token.
Install
forge install
cp .env.example .env
Build
forge build
Test
forge test
The private pool has an end-to-end run against a local anvil, which needs anvil and forge on your PATH:
cd zk && pnpm install && pnpm e2e
Deploy
Deployment uses CREATE2, so the token has the same address on every EVM network.
Bringing up a chain is registry, then registration, then token — run() does
all three and skips whichever has already happened.
forge script script/Love.s.sol:LoveScript --sig 'predict()' # print the address
WETH=0x4200000000000000000000000000000000000006 \
forge script script/Love.s.sol:LoveScript \
--rpc-url base_sepolia \
--account "$DEPLOYER_ACCOUNT" \
--broadcast \
--verify
WETH defaults to the OP Stack predeploy and is ignored once a wETH is
registered. Registration sends 1 wei, which stays in the registry.
To check determinism for real, deploy on two local chains and compare:
./script/multichain-check.sh
It starts two anvil nodes (chain IDs 31337 and 31338), gives them different
wETH implementations at different addresses, brings both up, and exits
non-zero if the two LOVE addresses differ. Override PORT_A, PORT_B,
CHAIN_ID_A, CHAIN_ID_B or SALT as needed.
Privacy
Besides the plain deposit(uint256), LOVE has a private pool. deposit(bytes32 commitment) locks 0.01 wETH against a note, and claim(proof, root, nullifierHash, recipient) pays its LOVE to any address whose owner can prove,
in zero knowledge, that they hold a note in the pool, without saying which. The
circuit and its Groth16 setup live in
zk/. The setup has a single contributor, so treat it as testnet-grade.
Full workflow, what the pool hides and its limits: see docs/ZK.md.
How wETH is chosen
LOVE is pegged to wETH, and that wETH is not a constructor argument. It is read
at construction time from WETHRegistry, which accepts an address only if its
EXTCODEHASH is one of ten reviewed wETH implementations and it survives a
one-wei deposit/withdraw round trip. Registration is permissionless, ownerless
and write-once.
Two things follow. A LOVE instance can never be bound to a typo, a lookalike
ERC-20 or an unreviewed wrapper. And because wETH is no longer in the creation
code, the creation code is byte-identical everywhere, so CREATE2 gives LOVE one
address on every chain instead of one per wETH deployment.
The allowlist covers 14 chains across 5 implementations:
| Chain | Chain ID | wETH implementation |
|---|---|---|
| Optimism | 10 | OP Stack legacy WETH9 |
| Base | 8453 | OP Stack legacy WETH9 |
| Mode | 34443 | OP Stack legacy WETH9 |
| Zora | 7777777 | OP Stack legacy WETH9 |
| World Chain | 480 | OP Stack legacy WETH9 |
| Lisk | 1135 | OP Stack legacy WETH9 |
| Ethereum | 1 | Canonical WETH9 |
| Linea | 59144 | Canonical WETH9 |
| Polygon zkEVM | 1101 | Canonical WETH9 |
| Unichain | 130 | OP Stack WETH |
| Soneium | 1868 | OP Stack WETH |
| Ink | 57073 | OP Stack WETH |
| Taiko | 167000 | Taiko |
| Scroll | 534352 | Scroll |
Deployment is blocked elsewhere, on purpose: the remaining chains either carry
bridged ETH with no deposit()/withdraw(), or put wETH behind an upgradeable
proxy, where a codehash commits to a forwarder rather than to behaviour. To
re-derive the list from chain state:
./script/weth-codehashes.sh
One caveat worth knowing before you trust an instance: the gate proves a
candidate is a reviewed wETH implementation, not that it is the wETH a
chain's ecosystem uses. Anyone can deploy a fresh WETH9 copy — same bytecode,
no liquidity — and register it first. Nothing is stealable, since it is real
WETH9 and fully redeemable, but check WETH() against the chain's canonical
address before treating a deployment as the real one.
Full design rationale, the allowlist's contents, and how to verify determinism
yourself: see docs/MULTICHAIN.md.
Docs
- Multichain deployment — CREATE2, the codehash allowlist, and verifying determinism
- ZK pool — sending LOVE privately with Groth16 proofs, and where the trust and capacity limits are
- Supported networks — where LOVE deploys today and what could be added
License
GPL-3.0
Contact
Julien Béranger (GitHub)
- Element: @julienbrg:matrix.org
- Farcaster: julien-
- Telegram: @julienbrg