Creating a Set Contract

December 3, 2024

This guide walks through creating a simple dice contract that demonstrates basic set functionality.

Initialize the Project

First, create a new Forge project and install dependencies:

forge init dice
cd dice
forge install everythingfun/v1-periphery

Write the Contract

Create a new file src/Dice.sol with the following code:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {SetBase} from "v1-periphery/sets/SetBase.sol";
import {ObjectMeta} from "v1-periphery/interfaces/core/types/ObjectMeta.sol";

contract Dice is SetBase {
    error ObjectIdOverflow();
    error InvalidFaceValue();

    uint64 private _nextId;

    constructor(uint64 kind) SetBase(kind) {
        _nextId = 1;
    }

    function mint() external returns (uint64) {
        uint256 randomFace = _random();
        return _mint(randomFace);
    }

    function mint(uint256 faceValue) external returns (uint64) {
        return _mint(faceValue);
    }

    function roll(uint64 id) external returns (uint256) {
        uint256 newFaceValue = _random();
        bytes32[] memory elems = new bytes32[](1);
        elems[0] = bytes32(newFaceValue);
        ObjectMeta memory meta = _update(id, elems);
        emit Updated(id, meta, elems);
        emit URI(uri(id), id);
        return newFaceValue;
    }

    function _mint(uint256 faceValue) internal returns (uint64) {
        if (faceValue < 1 || faceValue > 6) revert InvalidFaceValue();
        uint64 id = _nextId++;
        if (id >= type(uint64).max) revert ObjectIdOverflow();

        bytes32[] memory elems = new bytes32[](1);
        elems[0] = bytes32(faceValue);

        ObjectMeta memory meta = _create(id, elems);
        emit Created(id, meta, elems, msg.sender);
        emit TransferSingle(msg.sender, address(0), msg.sender, id, 1);
        return id;
    }

    function _random() private view returns (uint256) {
        return uint256(blockhash(block.number - 1)) % 6 + 1;
    }
}

Deploy and Register

  1. Build the contract:
forge build
  1. Deploy the contract (you'll need to add your specific deployment parameters):
forge create src/Dice.sol:Dice --constructor-args <KIND>
  1. Register your contract with the registry:
cast send sreg.0xpre.eth "register()(uint64)" --private-key <YOUR_KEY>

Interacting with the Contract

Minting Dice

To mint a new die with a random value:

cast send <CONTRACT_ADDRESS> "mint()" --private-key <YOUR_KEY>

Or mint with a specific face value (1-6):

cast send <CONTRACT_ADDRESS> "mint(uint256)" 3 --private-key <YOUR_KEY>

Rolling Dice

To roll an existing die:

cast send <CONTRACT_ADDRESS> "roll(uint64)" <TOKEN_ID> --private-key <YOUR_KEY>

Advanced Operations

For more complex operations like upgrading or transferring dice, use these commands:

# Transfer a die to another address
cast send <CONTRACT_ADDRESS> "transfer(uint64,address)" <TOKEN_ID> <RECIPIENT>

# Upgrade a die
cast send <CONTRACT_ADDRESS> "upgrade(uint64,uint32,uint32)" <TOKEN_ID> <NEW_LEVEL> <NEW_XP>

Remember to replace placeholder values (enclosed in <>) with actual parameters when using these commands.