Skip to content

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 dice
  • roll(): 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:

  1. A dice kind already created (see Register a Kind)
  2. A wallet set up and funded with tokens
  3. 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-set

Build to confirm it compiles:

cd dice-set
forge build

2. 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 build

Register the Set

1. Check the configuration

Ensure the Every CLI is configured for Sepolia:

every config show | jq .universes.sepolia

Example 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 files

Check 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 1

3. Register the data (as matter)

Create a shared data file:

echo '{}' > data.json

Register it:

every matter register data.json --account eve --network devnet

Output 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 sepolia

The 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 sepolia

The 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/meta

This 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
# 0x0144b03a

Facet assets are generated by the kind contract whenever the object is updated. For the object we just minted, the available facet assets are:

facetselectorasset URL
meta0x0144b03ahttps://every.bz/object/31337.22.3/1/0x0144b03a
picture0xeb0568a6https://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 sepolia

Sample 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:

facetselectorasset URL
meta0x0144b03ahttps://every.bz/object/31337.22.3/2/0x0144b03a
picture0xeb0568a6https://every.bz/object/31337.22.3/2/0xeb0568a6
©2025 every