Vouchers
ERC1155 compute vouchers, the (SKU, expiry) fungibility class, id packing and expiry buckets.
Vouchers
A voucher is a bearer claim on a specific unit of compute, redeemable until it expires. Vouchers are ERC1155 tokens.
Why ERC1155
The asset has an unusual shape. A single H100-GPU-hour is interchangeable with any other H100-GPU-hour of the same expiry, that is fungible behaviour, and you want holders to be able to hold 4,000 of them in one balance slot. But an H100-hour is not interchangeable with a CPU-hour, or with an H100-hour that expires three months later. That is non-fungible behaviour across classes.
ERC1155 gives exactly this: many token ids in one contract, each id with its own fungible supply. One deployment holds every SKU and every expiry, batch transfers cost one call, and the marketplace only needs to integrate with a single contract.
ERC20 would need a separate deployment per class, which fragments approvals and tooling. ERC721 would make each GPU-hour a unique object, which is wrong, nobody cares which GPU-hour they hold.
The fungibility class
The unit of fungibility is the pair (SKU, expiry bucket).
- SKU identifies what the voucher buys:
H100_SXM5_HOUR,CPU_CORE_HOUR,STORAGE_GB_MONTH. SKUs are defined in the registry with their unit and their kWh price. - Expiry bucket identifies when it stops being redeemable.
Two vouchers with the same SKU and the same bucket are the same token id and are perfectly substitutable. Change either field and you have a different id and a different market.
This matters for pricing. A voucher expiring next week and one expiring next quarter are genuinely different assets, the second carries more optionality, and merging them into one id would force the market to price an average, which serves nobody.
Id packing
The ERC1155 id is a uint256 with the class encoded directly into it. No storage lookup is needed to know what a token is.
| Bits | Width | Field | Notes |
|---|---|---|---|
| 255–248 | 8 | version | Encoding schema version |
| 247–184 | 64 | skuId | Index into the SKU registry |
| 183–152 | 32 | expiryBucket | Bucket index, see below |
| 151–0 | 152 | reserved | Must be zero |
function packId(uint8 version, uint64 skuId, uint32 expiryBucket)
internal
pure
returns (uint256)
{
return (uint256(version) << 248)
| (uint256(skuId) << 184)
| (uint256(expiryBucket) << 152);
}
function unpackId(uint256 id)
internal
pure
returns (uint8 version, uint64 skuId, uint32 expiryBucket)
{
version = uint8(id >> 248);
skuId = uint64(id >> 184);
expiryBucket = uint32(id >> 152);
require(uint152(id) == 0, "reserved bits set");
}
The reserved range is checked rather than ignored, so a malformed id cannot alias a valid one. The version byte leaves room to change the layout later without colliding with ids already in circulation.
Anyone, a wallet, an indexer, the marketplace, can decode a voucher's SKU and expiry from the id alone.
Expiry buckets
Vouchers do not carry an arbitrary timestamp. Expiry is quantised into buckets, and every voucher minted into the same bucket expires at the same instant.
expiryBucket = floor(expiryTimestamp / BUCKET_WIDTH)
expiresAt(id) = (expiryBucket + 1) · BUCKET_WIDTH
BUCKET_WIDTH is TBD and undetermined; a weekly or monthly width is the shape under consideration.
Quantisation is what makes the vouchers liquid. Free-form timestamps would mean a voucher minted on Tuesday afternoon is not fungible with one minted on Tuesday evening, and the order book would shatter into thousands of one-unit markets with no depth in any of them. Bucketing trades a small amount of precision, your voucher may expire slightly sooner than the maximum it could have, for a market that actually has a bid.
The cost is a visible step function: a voucher minted just before a bucket boundary has materially less remaining life than one minted just after, and the market should price that difference.
No refund on expiry
When a voucher expires it becomes non-redeemable and non-transferable. There is no refund, no rollover, and no residual claim on the points that were burned to mint it.
This is a deliberate consequence of what the voucher represents. The underlying asset is a reservation of capacity in a window of time. A GPU-hour in a week that has passed is not a thing that can be delivered late, the machine either ran your job that week or it sat idle. Idle time is not recoverable inventory. This is the same reason an airline seat has no salvage value once the aircraft has departed.
A refund mechanism would also break the economics on the supply side. Capacity has to be provisioned against outstanding vouchers. If every unredeemed voucher could be converted back into points and re-minted into a later bucket, the network would be carrying an unbounded, unpredictable liability that rolls forward forever, and it could never size hardware against demand.
Practically:
- Track your expiries. A wallet can decode them from the id.
- Sell what you will not use, well before the bucket closes. That is what the marketplace is for.
- Expect the market price of a bucket to decay toward zero as its expiry approaches. That decay is real and it is the holder's to manage.
Minting
Minting burns Energy Points and issues vouchers.
pointsBurned = skuKwhPrice(skuId) × quantity × (1 + mintFeeBps / 10_000)
The fee accrues to the protocol treasury and is one of the two real revenue lines described in Token. The fee rate is TBD.
Minting is only permitted into buckets the network has opened. You cannot mint arbitrarily far into the future, because the network cannot commit capacity it has not planned. The furthest open bucket is a governed parameter, TBD.