ProjectRegistry
0x0c4bb20ba79182b22a046f9eb2b80aa971e89a22
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
4,799 bytes
ABI entries
28
10 read · 4 write
License
none
Contract information
- Address
- 0x0c4bb20ba79182b22a046f9eb2b80aa971e89a22
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0xd03543d97B…F446Cd1418
- Creation tx
- 0xa17abd3eeb…8219c8e418
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (10)
DEFAULT_ADMIN_ROLE() → bytes32
PROJECT_FACTORY_ROLE() → bytes32
REGISTRY_VERSION() → uint32
genesisProjectNumber() → uint256
getProject(uint256) → tuple
getRoleAdmin(bytes32) → bytes32
hasRole(bytes32, address) → bool
nextProjectNumber() → uint256
projectNumberByContract(address) → uint256
supportsInterface(bytes4) → bool
Events (5)
ProjectIdentityRecordedProjectRegisteredRoleAdminChangedRoleGrantedRoleRevoked
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": "ContractAlreadyRegistered",
"type": "error"
},
{
"inputs": [],
"name": "GenesisProjectRequired",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidGenesisProject",
"type": "error"
},
{
"inputs": [],
"name": "InvalidProjectData",
"type": "error"
},
{
"inputs": [],
"name": "ProjectNumberChanged",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "metadataHash",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "normalizedNameHash",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "normalizedSymbolHash",
"type": "bytes32"
}
],
"name": "ProjectIdentityRecorded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "primaryContract",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "enum ProductType",
"name": "productType",
"type": "uint8"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"indexed": false,
"internalType": "bool",
"name": "isGenesis",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "isOfficial",
"type": "bool"
}
],
"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"
},
{
"inputs": [],
"name": "DEFAULT_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PROJECT_FACTORY_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "REGISTRY_VERSION",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "genesisProjectNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"name": "getProject",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "exists",
"type": "bool"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "enum ProductType",
"name": "productType",
"type": "uint8"
},
{
"internalType": "address",
"name": "primaryContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "metadataHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "normalizedNameHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "normalizedSymbolHash",
"type": "bytes32"
},
{
"internalType": "uint48",
"name": "createdAt",
"type": "uint48"
},
{
"internalType": "bool",
"name": "isGenesis",
"type": "bool"
},
{
"internalType": "bool",
"name": "isOfficial",
"type": "bool"
}
],
"internalType": "struct ProjectRegistry.ProjectRecord",
"name": "project",
"type": "tuple"
}
],
"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": [],
"name": "nextProjectNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "primaryContract",
"type": "address"
}
],
"name": "projectNumberByContract",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "expectedProjectNumber",
"type": "uint256"
},
{
"components": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "enum ProductType",
"name": "productType",
"type": "uint8"
},
{
"internalType": "address",
"name": "primaryContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "metadataHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "normalizedNameHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "normalizedSymbolHash",
"type": "bytes32"
},
{
"internalType": "bool",
"name": "isGenesis",
"type": "bool"
},
{
"internalType": "bool",
"name": "isOfficial",
"type": "bool"
}
],
"internalType": "struct ProjectRegistry.Registration",
"name": "registration",
"type": "tuple"
}
],
"name": "registerProject",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"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": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ProductType } from "../types/ProductType.sol";
/// @title Equifold Project Registry
/// @author Equifold
/// @notice Permanent protocol-level project identity independent from frontend and contract address.
contract ProjectRegistry is AccessControl {
error ContractAlreadyRegistered();
error GenesisProjectRequired();
error InvalidAddress();
error InvalidGenesisProject();
error InvalidProjectData();
error ProjectNumberChanged();
uint32 public constant REGISTRY_VERSION = 1;
bytes32 public constant PROJECT_FACTORY_ROLE = keccak256("PROJECT_FACTORY_ROLE");
struct Registration {
address creator;
ProductType productType;
address primaryContract;
bytes32 templateKey;
uint32 templateVersion;
bytes32 metadataHash;
bytes32 normalizedNameHash;
bytes32 normalizedSymbolHash;
bool isGenesis;
bool isOfficial;
}
struct ProjectRecord {
bool exists;
address creator;
ProductType productType;
address primaryContract;
bytes32 templateKey;
uint32 templateVersion;
bytes32 metadataHash;
bytes32 normalizedNameHash;
bytes32 normalizedSymbolHash;
uint48 createdAt;
bool isGenesis;
bool isOfficial;
}
uint256 public nextProjectNumber = 1;
uint256 public genesisProjectNumber;
mapping(uint256 projectNumber => ProjectRecord project) private _projects;
mapping(address primaryContract => uint256 projectNumber) public projectNumberByContract;
event ProjectRegistered(
uint256 indexed projectNumber,
address indexed primaryContract,
address indexed creator,
ProductType productType,
bytes32 templateKey,
uint32 templateVersion,
bool isGenesis,
bool isOfficial
);
event ProjectIdentityRecorded(
uint256 indexed projectNumber,
bytes32 metadataHash,
bytes32 normalizedNameHash,
bytes32 normalizedSymbolHash
);
constructor(
address admin
) {
if (admin == address(0)) revert InvalidAddress();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
/// @notice Registers the next project. Project one must be the generic Genesis launch.
function registerProject(
uint256 expectedProjectNumber,
Registration calldata registration
) external onlyRole(PROJECT_FACTORY_ROLE) returns (uint256 projectNumber) {
projectNumber = nextProjectNumber;
if (expectedProjectNumber != projectNumber) revert ProjectNumberChanged();
_validateRegistration(projectNumber, registration);
_storeProject(projectNumber, registration);
++nextProjectNumber;
emit ProjectRegistered(
projectNumber,
registration.primaryContract,
registration.creator,
registration.productType,
registration.templateKey,
registration.templateVersion,
registration.isGenesis,
registration.isOfficial
);
emit ProjectIdentityRecorded(
projectNumber,
registration.metadataHash,
registration.normalizedNameHash,
registration.normalizedSymbolHash
);
}
function _validateRegistration(
uint256 projectNumber,
Registration calldata registration
) private view {
if (
registration.creator == address(0) || registration.primaryContract == address(0)
|| registration.primaryContract.code.length == 0
) revert InvalidAddress();
if (
registration.templateKey == bytes32(0) || registration.templateVersion == 0
|| registration.metadataHash == bytes32(0)
|| registration.normalizedNameHash == bytes32(0)
|| registration.normalizedSymbolHash == bytes32(0)
) revert InvalidProjectData();
if (projectNumberByContract[registration.primaryContract] != 0) {
revert ContractAlreadyRegistered();
}
if (projectNumber == 1 && !registration.isGenesis) revert GenesisProjectRequired();
if (registration.isGenesis && (projectNumber != 1 || genesisProjectNumber != 0)) {
revert InvalidGenesisProject();
}
}
function _storeProject(
uint256 projectNumber,
Registration calldata registration
) private {
_projects[projectNumber] = ProjectRecord({
exists: true,
creator: registration.creator,
productType: registration.productType,
primaryContract: registration.primaryContract,
templateKey: registration.templateKey,
templateVersion: registration.templateVersion,
metadataHash: registration.metadataHash,
normalizedNameHash: registration.normalizedNameHash,
normalizedSymbolHash: registration.normalizedSymbolHash,
createdAt: uint48(block.timestamp),
isGenesis: registration.isGenesis,
isOfficial: registration.isOfficial
});
projectNumberByContract[registration.primaryContract] = projectNumber;
if (registration.isGenesis) genesisProjectNumber = projectNumber;
}
function getProject(
uint256 projectNumber
) external view returns (ProjectRecord memory project) {
project = _projects[projectNumber];
if (!project.exists) revert InvalidProjectData();
}
}
Chain explorer6447msChain node92ms