WORM is interesting, but the real building block behind it is BETH.
BETH itself is simply an ERC-20 token that can be minted by proving that you burned ETH at some point, somewhere. You do not need to show the ERC-20 contract where the burn happened or which account performed it. Instead, you provide a zero-knowledge proof demonstrating that the burn occurred, and the contract will mint an equal amount of BETH tokens for you.
Here is the twist: if a BETH/ETH market exists, you can sell the BETH you just minted and receive ETH in return. This makes the BETH concept a very attractive EIP-7503-style privacy system, with an extremely large anonymity set from day one.
However, there is an important challenge: how do we create demand for BETH?
This is where WORM comes in. WORM is a secondary, hard-capped ERC-20 token with a Bitcoin-like mining system powered by BETH instead of electricity. However, WORM does not need to be the only project that utilizes BETH.
Our vision is to promote BETH as infrastructure so that new projects can grow on top of it. The most obvious idea right now is to use BETH as proof-of-burn mining fuel. Developers can build WORM-like cryptoassets with different emission algorithms, marketing strategies, and potentially novel mechanisms.
In this article, we will explore how to build a WORM-like asset in Solidity. We will call this new asset SLUG.
We assume that:
- You already understand how BETH works.
- You already have some BETH in your wallet to consume for SLUG mining.
So let’s begin.
To keep the code simple, we will use a simplified specification compared to WORM:
- SLUG uses 30-minute epochs
- 100 SLUG are generated every epoch (no reward decay)
- Generated SLUG tokens are distributed among BETH consumers based on their share of BETH contributed in that epoch
The SLUG contract needs to know two things:
- The timestamp when SLUG mining begins
- The address of the BETH contract
BETH is deployed on Ethereum mainnet at:
0x5624344235607940d4d4EE76Bf8817d403EB9Cf8
We begin with the constructor:
IERC20 public immutable bethContract;
uint256 public immutable startingTimestamp;
constructor(IERC20 _bethContract, uint256 _startingTimestamp)
ERC20("SLUG", "SLUG")
{
bethContract = _bethContract;
startingTimestamp = _startingTimestamp;
}In several parts of the contract, we need to know which epoch is currently active. To handle this, we define a small view function that calculates the current epoch based on the starting timestamp and the epoch duration.
In our design, each epoch lasts 30 minutes (1800 seconds).
uint256 constant EPOCH_DURATION = 1800 seconds;
function currentEpoch() public view returns (uint256) {
require(block.timestamp >= startingTimestamp, "Mining has not started yet!");
return (block.timestamp - startingTimestamp) / EPOCH_DURATION;
}Next, we implement the participate function.
Inspired by the WORM implementation, users specify:
- the amount of BETH to contribute per epoch
- the number of epochs
The contract keeps track of two values:
- The total amount of BETH contributed per epoch
- The amount of BETH contributed per epoch per user
function participate(uint256 _amountPerEpoch, uint256 _numEpochs) external {
require(_numEpochs != 0, "Invalid epoch number.");
uint256 currEpoch = currentEpoch();
for (uint256 i = 0; i < _numEpochs; i++) {
epochTotal[currEpoch + i] += _amountPerEpoch;
epochUser[currEpoch + i][msg.sender] += _amountPerEpoch;
}
require(
bethContract.transferFrom(msg.sender, address(this), _numEpochs * _amountPerEpoch),
"TF"
);
emit Participated(msg.sender, currEpoch, _numEpochs, _amountPerEpoch);
}With this structure, calculating a user’s participation ratio for a specific epoch becomes straightforward:
epochUser[i][user] / epochTotal[i]
Claiming rewards follows a similar pattern.
The contract:
- Loops through the selected epochs
- Calculates the user’s reward by multiplying the epoch reward by the participation ratio
- Sums all rewards
- Mints the resulting amount
- Clears the user’s epoch data to prevent double claiming
uint256 constant EPOCH_REWARD = 100 ether;
function claim(uint256 _startingEpoch, uint256 _numEpochs) public {
require(
_startingEpoch + _numEpochs <= currentEpoch(),
"Cannot claim an ongoing epoch!"
);
uint256 mintAmount = 0;
for (uint256 i = 0; i < _numEpochs; i++) {
uint256 total = epochTotal[_startingEpoch + i];
if (total > 0) {
uint256 user = epochUser[_startingEpoch + i][_user];
mintAmount += EPOCH_REWARD * user / total;
}
}
for (uint256 i = 0; i < _numEpochs; i++) {
epochUser[_startingEpoch + i][msg.sender] = 0;
}
_mint(msg.sender, mintAmount);
emit Claimed(msg.sender, _startingEpoch, _numEpochs, mintAmount);
}And that’s it! 🎉
With just these few components, you can build a WORM-style proof-of-burn mining token that distributes rewards proportionally to BETH consumption.
From here, you could extend the design with features such as:
- reward decay schedules
- mining difficulty adjustments
- staking multipliers
- or entirely new emission mechanisms
The key idea remains simple: BETH acts as the fuel, and your token defines how that fuel is converted into rewards.