Register a Set
This walkthrough covers writing a set contract, registering it in the Set Registry, and minting objects from the set.
In this guide, you’ll implement a dice contract. The contract exposes two key methods:
mint(): mint a new diceroll(): roll the dice to a new face value
The set is built on top of an existing kind (which could be developed by you, others, or even an AI).
Before You Begin
Make sure you have:
- A dice kind already created (see Register a Kind)
- A wallet set up and funded with tokens
- Foundry installed (for scaffolding and deploying contracts)
Write a Set Contract
1. Initialize a project
Start a new set contract project from the template:
forge init --template everytemplate/set dice-setBuild to confirm it compiles:
cd dice-set
forge build2. Implement contract logic
Edit src/DiceSet.sol. A complete example is available in the
dice-set repository.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {ObjectIdAuto} from "@everyprotocol/periphery/libraries/Allocator.sol";
import {ISetRegistry, SetRegistryAdmin} from "@everyprotocol/periphery/utils/SetRegistryAdmin.sol";
import {ISetRegistryHook, SetContext, SetRegistryHook} from "@everyprotocol/periphery/utils/SetRegistryHook.sol";
import {Descriptor, ISet, SetSolo} from "@everyprotocol/periphery/utils/SetSolo.sol";
contract DiceSet is SetSolo, SetRegistryHook, SetRegistryAdmin {
using ObjectIdAuto for ObjectIdAuto.Storage;
error KindNotSpecified();
error SetNotAssigned();
ObjectIdAuto.Storage internal _idManager;
constructor(address setRegistry, uint64 kindId, uint32 kindRev) SetRegistryHook(setRegistry) {
if (kindRev == 0 || kindId == 0) revert KindNotSpecified();
SetContext.setKindId(kindId);
SetContext.setKindRev(kindRev);
}
function mint(address to, uint64 id0) external returns (uint64 id, Descriptor memory desc) {
(uint64 setId, uint32 setRev) = (SetContext.getSetId(), SetContext.getSetRev());
if (setId == 0 || setRev == 0) revert SetNotAssigned();
(uint64 kindId, uint32 kindRev) = (SetContext.getKindId(), SetContext.getKindRev());
if (kindId == 0 || kindRev == 0) revert KindNotSpecified();
desc = Descriptor({
traits: 0,
rev: 1,
setRev: setRev,
kindRev: kindRev,
kindId: kindId,
setId: setId
});
id = _idManager.allocate(id0);
bytes32 ;
elems[0] = _roll();
_create(to, id, desc, elems);
_postCreate(to, id, desc, elems);
}
function roll(uint64 id, uint256 face) external returns (Descriptor memory od) {
bytes32 ;
elems[0] = bytes32(face);
od = _update(id, elems);
_postUpdate(id, od, elems);
}
function roll(uint64 id) external returns (Descriptor memory od) {
bytes32 ;
elems[0] = _roll();
od = _update(id, elems);
_postUpdate(id, od, elems);
}
function _roll() internal view returns (bytes32) {
return keccak256(abi.encodePacked(block.prevrandao, tx.origin, gasleft()));
}
function supportsInterface(bytes4 interfaceId) external pure override(SetSolo, SetRegistryHook) returns (bool) {
return interfaceId == type(ISetRegistryHook).interfaceId || SetSolo._supportsInterface(interfaceId);
}
function _objectURI() internal view virtual override returns (string memory) {
ISetRegistry setr = ISetRegistry(SetContext.getSetRegistry());
return setr.setURI(SetContext.getSetId());
}
}Build the contract:
forge buildRegister the Set
1. Check the configuration
Ensure the Every CLI is configured for Sepolia:
every config show | jq .universes.sepoliaExample output:
{
"id": 31337,
"rpc": "http://127.0.0.1:8545",
"explorer": "http://127.0.0.1:8545",
"observer": "dev",
"contracts": {
"SetRegistry": "0x854C35Fd2b65fE9fcE71dddE91De8c3e1A7Dc8Ae",
"OmniRegistry": "0x4b7fc108F2c4fCDD990240448D2eED8034713c7D",
"KindRegistry": "0x7A8B3E5A9c227858C5917b7de8ba1684Cd868630",
"ElementRegistry": "0x8De1EE1dbAE2Ffd1CAe1e6bA83E6cAede1461507",
"ObjectMinter": "0x12CaBC370b316F247126F3Fab529Ee25e03aE226"
}
}The Every CLI ships with several default configs. If none are found, you can add a .every.toml file either in the current directory or in your home directory.
Use the command below to see all configuration files that the CLI checks:
every config filesCheck the first file in the list as a reference for your own configuration. You don’t need to copy the entire contents — configs are merged automatically, and values from later files override earlier ones.
2. Deploy the contract
Deploy using Foundry. Replace --rpc-url and the first constructor argument with values from the config:
forge create src/DiceSet.sol:DiceSet \
--rpc-url http://127.0.0.1:8545 \
--account eve \
--constructor-args 0x854C35Fd2b65fE9fcE71dddE91De8c3e1A7Dc8Ae 17 13. Register the data (as matter)
Create a shared data file:
echo '{}' > data.jsonRegister it:
every matter register data.json --account eve --network devnetOutput includes its hash:
Register matter: form=1 mime=application/json blob=3B data.json
MatterRegistered ['...','0x56c792cfe063dde75e9e6afb371542ea141c62c342820ce9b0394992c7f40d35']4. Register the set
Register the deployed contract in the Set Registry:
every set register \
0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 \
0x56c792cfe063dde75e9e6afb371542ea141c62c342820ce9b0394992c7f40d35 \
--foundry --account eve --universe sepoliaThe two arguments are:
<code>— the address of the set contract<data>— the matter hash of the shared data
Sample output:
Transaction sending...
Transaction sent: 0x66195f4628e1bf667415674b4885c5271c137351ecd9be4e29dc8792d88bd3f2
Waiting for confirmation...
Confirmed in: block 49600, hash 0xfd0fab254c30c9f9d066f4e009607a717f96c73860e53a891a9bcf6a3a35b084
SetRegistered {id:22n,desc:{traits:0,rev:1,kindRev:1,setRev:1,kindId:1n,setId:1n},code:'0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44',data:'0x56c792cfe063dde75e9e6afb371542ea141c62c342820ce9b0394992c7f40d35',owner:'0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44'}The SetRegistered event confirms that the set has been successfully registered. In this example, the set was assigned an ID of 22.
Interact with Objects
1. Mint objects
Mint a new object from the set by calling its mint method.
every object send --sig 'mint(address, uint64)' \
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 17.0 \
--foundry --account eve --universe sepoliaThe exact signature may vary depending on the contract, but the every object send command makes the process seamless.
The command accepts an SID (semi OID, {set}.{id}) parameter. It queries the Set Registry to resolve the address of the set contract, then passes the object ID as the actual argument to the function call.
Sample output:
Transaction sending...
Transaction sent: 0x9aff19c936d4a893319c6414b911085be6977bfa4004805b39e3b3bfa9712faf
Transaction mining...
Waiting for confirmation...
Confirmed in: block 50685, hash 0x3219ae60b276bf1ff4aebba3170c7ab1f7d862700020614c1d89ee458e72424f
Created {id:3n,od:{traits:0,rev:1,kindRev:1,setRev:1,kindId:17n,setId:22n},elems:['0x9ac9c4dd279b35d6eb6c495b5d3441e9d39f003d0cc6b11788da6ffe4e2dd6d8'],owner:'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'}The Created event confirms that a new object was successfully minted. In this example, the object has ID 3 and belongs to Set 22.
2. View objects
You can view an object’s metadata URI with the object uri command:
every object uri 22.3
# http://every.bz/31337.22.3/1/metaThis URI points to the object’s metadata asset, which can be accessed through Every Network gateways. Object assets are also called facet assets. They can be referenced either by their facet selector or, for convenience, by their facet name:
https://{gateway}/object/{oid}/{rev}/{selector}https://{gateway}/object/{oid}/{rev}/{name}
Similar to Ethereum function selectors, facet selectors are derived from the facet (asset) function signature.
For example, the selector for meta() is computed as:
cast keccak "meta" | cut -c1-10
# 0x0144b03aFacet assets are generated by the kind contract whenever the object is updated. For the object we just minted, the available facet assets are:
| facet | selector | asset URL |
|---|---|---|
| meta | 0x0144b03a | https://every.bz/object/31337.22.3/1/0x0144b03a |
| picture | 0xeb0568a6 | https://every.bz/object/31337.22.3/1/0xeb0568a6 |
3. Update objects
You can update the newly created object by invoking its roll method.
every object send --sig 'roll(uint64)' \
17.1 \
--foundry --account eve --universe sepoliaSample output:
Transaction sending...
Transaction sent: 0x32d3abef057a353913eccd402f846308bb955a754f9841899081a9f706105f06
Transaction mining...
Waiting for confirmation...
Confirmed in: block 51000, hash 0x9011d33e4c3049d9c19541a7e2480e4fbd1f14b17dcf678c8fc4758b75f341a1
Updated {id:3n,od:{traits:0,rev:2,kindRev:1,setRev:1,kindId:17n,setId:22n},elems:['0x943ad65995e477d9a99e8649ae20c6c5687821567867d414aac0f9c1cdd8bca8']}The Updated event confirms the object now has revision 2. You can inspect its updated facet assets via the gateway:
| facet | selector | asset URL |
|---|---|---|
| meta | 0x0144b03a | https://every.bz/object/31337.22.3/2/0x0144b03a |
| picture | 0xeb0568a6 | https://every.bz/object/31337.22.3/2/0xeb0568a6 |