LaunchTemplateRegistry

0x1c781892dbe4dbaf2637259e4963b3cc41eb46a0

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
4,589 bytes
ABI entries
31
13 read · 5 write
License
none

Contract information

Address
0x1c781892dbe4dbaf2637259e4963b3cc41eb46a0
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0x46d4da66c5…7172f34b15

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (13)

DEFAULT_ADMIN_ROLE()bytes32
MAX_CREATOR_FEE_BPS()uint16
MAX_LAUNCH_FEE_WEI()uint256
MAX_TOKEN_DECIMALS()uint8
MAX_TOKEN_SUPPLY()uint256
MIN_TOKEN_DECIMALS()uint8
REGISTRY_VERSION()uint32
TEMPLATE_MANAGER_ROLE()bytes32
getRoleAdmin(bytes32)bytes32
getTemplate(bytes32, uint32)tuple
hasRole(bytes32, address)bool
latestVersion(bytes32)uint32
supportsInterface(bytes4)bool

Events (5)

RoleAdminChangedRoleGrantedRoleRevokedTemplateAvailabilityUpdatedTemplateVersionAdded

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": "InvalidAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidTemplate",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidVersion",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TemplateAlreadyExists",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TemplateNotFound",
    "type": "error"
  },
  {
    "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": "bytes32",
        "name": "templateKey",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "TemplateAvailabilityUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "templateKey",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "configurationHash",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "TemplateVersionAdded",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_CREATOR_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_LAUNCH_FEE_WEI",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_TOKEN_DECIMALS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_TOKEN_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_TOKEN_DECIMALS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "REGISTRY_VERSION",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TEMPLATE_MANAGER_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "templateKey",
        "type": "bytes32"
      },
      {
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "tokenSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint8",
            "name": "tokenDecimals",
            "type": "uint8"
          },
          {
            "internalType": "bool",
            "name": "burnable",
            "type": "bool"
          },
          {
            "internalType": "uint256",
            "name": "launchFeeWei",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "creatorFeeBps",
            "type": "uint16"
          },
          {
            "internalType": "uint256",
            "name": "stakeRequirement",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "curveConfigHash",
            "type": "bytes32"
          },
          {
            "internalType": "bytes32",
            "name": "fairLaunchConfigHash",
            "type": "bytes32"
          },
          {
            "internalType": "bool",
            "name": "isGenesis",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "isOfficial",
            "type": "bool"
          }
        ],
        "internalType": "struct LaunchTemplateRegistry.TemplateConfig",
        "name": "config",
        "type": "tuple"
      },
      {
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "addTemplateVersion",
    "outputs": [],
    "stateMutability": "nonpayable",
    "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": "templateKey",
        "type": "bytes32"
      },
      {
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      }
    ],
    "name": "getTemplate",
    "outputs": [
      {
        "components": [
          {
            "internalType": "bool",
            "name": "exists",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "enabled",
            "type": "bool"
          },
          {
            "internalType": "uint32",
            "name": "version",
            "type": "uint32"
          },
          {
            "internalType": "uint256",
            "name": "tokenSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint8",
            "name": "tokenDecimals",
            "type": "uint8"
          },
          {
            "internalType": "bool",
            "name": "burnable",
            "type": "bool"
          },
          {
            "internalType": "uint256",
            "name": "launchFeeWei",
            "type": "uint256"
          },
          {
            "internalType": "uint16",
            "name": "creatorFeeBps",
            "type": "uint16"
          },
          {
            "internalType": "uint256",
            "name": "stakeRequirement",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "curveConfigHash",
            "type": "bytes32"
          },
          {
            "internalType": "bytes32",
            "name": "fairLaunchConfigHash",
            "type": "bytes32"
          },
          {
            "internalType": "bool",
            "name": "isGenesis",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "isOfficial",
            "type": "bool"
          }
        ],
        "internalType": "struct LaunchTemplateRegistry.StandardTokenTemplate",
        "name": "template",
        "type": "tuple"
      }
    ],
    "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": "bytes32",
        "name": "templateKey",
        "type": "bytes32"
      }
    ],
    "name": "latestVersion",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "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": "bytes32",
        "name": "templateKey",
        "type": "bytes32"
      },
      {
        "internalType": "uint32",
        "name": "version",
        "type": "uint32"
      },
      {
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "setTemplateEnabled",
    "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";

/// @title Equifold Launch Template Registry
/// @author Equifold
/// @notice Append-only standard-token templates with explicit versions and bounded parameters.
contract LaunchTemplateRegistry is AccessControl {
    error InvalidAddress();
    error InvalidTemplate();
    error InvalidVersion();
    error TemplateAlreadyExists();
    error TemplateNotFound();

    uint16 public constant MAX_CREATOR_FEE_BPS = 1000;
    uint256 public constant MAX_LAUNCH_FEE_WEI = 10 ether;
    uint256 public constant MAX_TOKEN_SUPPLY = 1e36;
    uint8 public constant MIN_TOKEN_DECIMALS = 6;
    uint8 public constant MAX_TOKEN_DECIMALS = 18;
    uint32 public constant REGISTRY_VERSION = 1;

    bytes32 public constant TEMPLATE_MANAGER_ROLE = keccak256("TEMPLATE_MANAGER_ROLE");

    struct TemplateConfig {
        uint256 tokenSupply;
        uint8 tokenDecimals;
        bool burnable;
        uint256 launchFeeWei;
        uint16 creatorFeeBps;
        uint256 stakeRequirement;
        bytes32 curveConfigHash;
        bytes32 fairLaunchConfigHash;
        bool isGenesis;
        bool isOfficial;
    }

    struct StandardTokenTemplate {
        bool exists;
        bool enabled;
        uint32 version;
        uint256 tokenSupply;
        uint8 tokenDecimals;
        bool burnable;
        uint256 launchFeeWei;
        uint16 creatorFeeBps;
        uint256 stakeRequirement;
        bytes32 curveConfigHash;
        bytes32 fairLaunchConfigHash;
        bool isGenesis;
        bool isOfficial;
    }

    mapping(bytes32 templateKey => uint32 version) public latestVersion;
    mapping(bytes32 templateKey => mapping(uint32 version => StandardTokenTemplate template))
        private _templates;

    event TemplateVersionAdded(
        bytes32 indexed templateKey,
        uint32 indexed version,
        bytes32 indexed configurationHash,
        bool enabled
    );
    event TemplateAvailabilityUpdated(
        bytes32 indexed templateKey, uint32 indexed version, bool enabled
    );

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

    /// @notice Adds the next immutable version of a launch template.
    function addTemplateVersion(
        bytes32 templateKey,
        uint32 version,
        TemplateConfig calldata config,
        bool enabled
    ) external onlyRole(TEMPLATE_MANAGER_ROLE) {
        if (templateKey == bytes32(0)) revert InvalidTemplate();
        if (version == 0 || version != latestVersion[templateKey] + 1) revert InvalidVersion();
        if (_templates[templateKey][version].exists) revert TemplateAlreadyExists();
        if (
            config.tokenSupply == 0 || config.tokenSupply > MAX_TOKEN_SUPPLY
                || config.tokenDecimals < MIN_TOKEN_DECIMALS
                || config.tokenDecimals > MAX_TOKEN_DECIMALS
                || config.launchFeeWei > MAX_LAUNCH_FEE_WEI
                || config.creatorFeeBps > MAX_CREATOR_FEE_BPS
                || config.curveConfigHash == bytes32(0) || config.fairLaunchConfigHash == bytes32(0)
                || (config.isGenesis && !config.isOfficial)
        ) revert InvalidTemplate();

        _templates[templateKey][version] = StandardTokenTemplate({
            exists: true,
            enabled: enabled,
            version: version,
            tokenSupply: config.tokenSupply,
            tokenDecimals: config.tokenDecimals,
            burnable: config.burnable,
            launchFeeWei: config.launchFeeWei,
            creatorFeeBps: config.creatorFeeBps,
            stakeRequirement: config.stakeRequirement,
            curveConfigHash: config.curveConfigHash,
            fairLaunchConfigHash: config.fairLaunchConfigHash,
            isGenesis: config.isGenesis,
            isOfficial: config.isOfficial
        });
        latestVersion[templateKey] = version;

        emit TemplateVersionAdded(templateKey, version, keccak256(abi.encode(config)), enabled);
    }

    /// @notice Enables or disables new launches without mutating the template configuration.
    function setTemplateEnabled(
        bytes32 templateKey,
        uint32 version,
        bool enabled
    ) external onlyRole(TEMPLATE_MANAGER_ROLE) {
        StandardTokenTemplate storage template = _templates[templateKey][version];
        if (!template.exists) revert TemplateNotFound();
        template.enabled = enabled;
        emit TemplateAvailabilityUpdated(templateKey, version, enabled);
    }

    function getTemplate(
        bytes32 templateKey,
        uint32 version
    ) external view returns (StandardTokenTemplate memory template) {
        template = _templates[templateKey][version];
        if (!template.exists) revert TemplateNotFound();
    }
}
Chain explorer4871msChain node72ms