CreatorSplitRegistryV2

0xca358f414503e3f3e2b39be9549b188798ba1669

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
4,912 bytes
ABI entries
33
9 read · 8 write
License
none

Contract information

Address
0xca358f414503e3f3e2b39be9549b188798ba1669
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x7f5f316859…66a51dbe7e

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (9)

DEFAULT_ADMIN_ROLE()bytes32
MAX_RECIPIENTS()uint8
REGISTRAR_ROLE()bytes32
creatorOf(uint256)address
currentRecipients(uint256)address[], uint16[]
feeMode(uint256)uint8
getRoleAdmin(bytes32)bytes32
hasRole(bytes32, address)bool
supportsInterface(bytes4)bool

Events (6)

FeeModeUpdatedProjectRegisteredRoleAdminChangedRoleGrantedRoleRevokedSplitUpdated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "admin",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AccessControlBadConfirmation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "neededRole",
        "type": "bytes32"
      }
    ],
    "name": "AccessControlUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AlreadyRegistered",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BadRecipientCount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BadWeightSum",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LengthMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotCreator",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UnknownProject",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "enum CreatorSplitRegistryV2.FeeMode",
        "name": "mode",
        "type": "uint8"
      }
    ],
    "name": "FeeModeUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      }
    ],
    "name": "ProjectRegistered",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousAdminRole",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newAdminRole",
        "type": "bytes32"
      }
    ],
    "name": "RoleAdminChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleGranted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleRevoked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address[]",
        "name": "recipients",
        "type": "address[]"
      },
      {
        "indexed": false,
        "internalType": "uint16[]",
        "name": "bps",
        "type": "uint16[]"
      }
    ],
    "name": "SplitUpdated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_RECIPIENTS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "REGISTRAR_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "internalType": "enum CreatorSplitRegistryV2.FeeMode",
        "name": "mode",
        "type": "uint8"
      }
    ],
    "name": "adminSetFeeMode",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "internalType": "address[]",
        "name": "recipients",
        "type": "address[]"
      },
      {
        "internalType": "uint16[]",
        "name": "bps",
        "type": "uint16[]"
      }
    ],
    "name": "adminSetSplit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      }
    ],
    "name": "creatorOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      }
    ],
    "name": "currentRecipients",
    "outputs": [
      {
        "internalType": "address[]",
        "name": "recipients",
        "type": "address[]"
      },
      {
        "internalType": "uint16[]",
        "name": "bps",
        "type": "uint16[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      }
    ],
    "name": "feeMode",
    "outputs": [
      {
        "internalType": "enum CreatorSplitRegistryV2.FeeMode",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      }
    ],
    "name": "getRoleAdmin",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "grantRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "hasRole",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      }
    ],
    "name": "registerProject",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "callerConfirmation",
        "type": "address"
      }
    ],
    "name": "renounceRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "revokeRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "internalType": "enum CreatorSplitRegistryV2.FeeMode",
        "name": "mode",
        "type": "uint8"
      }
    ],
    "name": "setFeeMode",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "projectNumber",
        "type": "uint256"
      },
      {
        "internalType": "address[]",
        "name": "recipients",
        "type": "address[]"
      },
      {
        "internalType": "uint16[]",
        "name": "bps",
        "type": "uint16[]"
      }
    ],
    "name": "updateSplit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";

/// @title Equifold Creator Split Registry V2
/// @notice Push-model creator fee-split registry shared by the gen4 unified launch verticals. Each
///         gen4 module factory registers its project's creator once, holding `REGISTRAR_ROLE`; the
///         registered creator may then push a multi-recipient split at will, and the admin can
///         override it at any time. Fee-pushers read {currentRecipients} at payout time — there is
///         no epoch weighting or on-chain payout math here, unlike {FeeShareRegistry}.
contract CreatorSplitRegistryV2 is AccessControl {
    error AlreadyRegistered();
    error BadRecipientCount();
    error BadWeightSum();
    error LengthMismatch();
    error NotCreator();
    error UnknownProject();
    error ZeroAddress();

    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
    uint8 public constant MAX_RECIPIENTS = 10;
    uint16 internal constant BPS_DENOMINATOR = 10_000;

    /// @notice The creator-chosen disposition of the creator (70%) leg of every fee crank, read once
    ///         per crank by all three gen4 launch modules. `WALLET == 0` is the storage default so an
    ///         existing project that never set a mode reads back {FeeMode.WALLET} (push over the split).
    ///         BURN buys+burns the creator leg; COMPOUND pairs it back into the locked position. The
    ///         platform (30%) leg is treasury-direct and unaffected by this choice in every mode.
    enum FeeMode {
        WALLET,
        BURN,
        COMPOUND
    }

    mapping(uint256 projectNumber => address creator) public creatorOf;
    mapping(uint256 projectNumber => address[] recipients) private _recipients;
    mapping(uint256 projectNumber => uint16[] bps) private _bps;
    mapping(uint256 projectNumber => FeeMode mode) private _feeMode;

    event ProjectRegistered(uint256 indexed projectNumber, address indexed creator);
    event SplitUpdated(uint256 indexed projectNumber, address[] recipients, uint16[] bps);
    event FeeModeUpdated(uint256 indexed projectNumber, FeeMode mode);

    constructor(
        address admin
    ) {
        if (admin == address(0)) revert ZeroAddress();
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
    }

    /// @notice Registers `projectNumber`'s creator. Called once by a gen4 module factory holding
    ///         `REGISTRAR_ROLE` at launch time.
    function registerProject(
        uint256 projectNumber,
        address creator
    ) external onlyRole(REGISTRAR_ROLE) {
        if (creatorOf[projectNumber] != address(0)) revert AlreadyRegistered();
        if (creator == address(0)) revert ZeroAddress();
        creatorOf[projectNumber] = creator;
        emit ProjectRegistered(projectNumber, creator);
    }

    /// @notice Creator-driven split update, callable at any time by `projectNumber`'s registered
    ///         creator.
    function updateSplit(
        uint256 projectNumber,
        address[] calldata recipients,
        uint16[] calldata bps
    ) external {
        if (msg.sender != creatorOf[projectNumber]) revert NotCreator();
        _setSplit(projectNumber, recipients, bps);
    }

    /// @notice Admin override of `projectNumber`'s split, usable at any time regardless of who the
    ///         registered creator is.
    function adminSetSplit(
        uint256 projectNumber,
        address[] calldata recipients,
        uint16[] calldata bps
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _setSplit(projectNumber, recipients, bps);
    }

    /// @notice The split fee-pushers pay out against. Defaults to 100% to the creator until either
    ///         the creator or the admin sets an explicit multi-recipient split.
    function currentRecipients(
        uint256 projectNumber
    ) external view returns (address[] memory recipients, uint16[] memory bps) {
        address creator = creatorOf[projectNumber];
        if (creator == address(0)) revert UnknownProject();
        if (_recipients[projectNumber].length == 0) {
            recipients = new address[](1);
            recipients[0] = creator;
            bps = new uint16[](1);
            bps[0] = BPS_DENOMINATOR;
            return (recipients, bps);
        }
        return (_recipients[projectNumber], _bps[projectNumber]);
    }

    /// @notice The creator-of-record's live fee-disposition choice for `projectNumber`. Reverts
    ///         {UnknownProject} for an unregistered project, exactly like {currentRecipients}; an
    ///         un-set (but registered) project reads back {FeeMode.WALLET}.
    function feeMode(
        uint256 projectNumber
    ) external view returns (FeeMode) {
        if (creatorOf[projectNumber] == address(0)) revert UnknownProject();
        return _feeMode[projectNumber];
    }

    /// @notice Creator-driven fee-mode update, callable at any time by `projectNumber`'s registered
    ///         creator. No on-chain change limit — the wizard/dashboard gates one change, same policy
    ///         as splits.
    function setFeeMode(
        uint256 projectNumber,
        FeeMode mode
    ) external {
        if (msg.sender != creatorOf[projectNumber]) revert NotCreator();
        _setFeeMode(projectNumber, mode);
    }

    /// @notice Admin override of `projectNumber`'s fee mode, usable at any time regardless of who the
    ///         registered creator is (mirrors {adminSetSplit}).
    function adminSetFeeMode(
        uint256 projectNumber,
        FeeMode mode
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _setFeeMode(projectNumber, mode);
    }

    function _setFeeMode(
        uint256 projectNumber,
        FeeMode mode
    ) private {
        _feeMode[projectNumber] = mode;
        emit FeeModeUpdated(projectNumber, mode);
    }

    function _setSplit(
        uint256 projectNumber,
        address[] calldata recipients,
        uint16[] calldata bps
    ) private {
        uint256 length = recipients.length;
        if (length == 0 || length > MAX_RECIPIENTS) revert BadRecipientCount();
        if (length != bps.length) revert LengthMismatch();
        uint256 sum;
        for (uint256 i; i < length; ++i) {
            if (recipients[i] == address(0)) revert ZeroAddress();
            sum += bps[i];
        }
        if (sum != BPS_DENOMINATOR) revert BadWeightSum();
        _recipients[projectNumber] = recipients;
        _bps[projectNumber] = bps;
        emit SplitUpdated(projectNumber, recipients, bps);
    }
}
Chain explorer2919msChain node96ms