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
- Creator
- 0xd03543d97B…F446Cd1418
- 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