Transient Labs Developer Docs
  • 👋Welcome!
  • 📑TL Creator Contracts
    • Creator Contracts Overview
    • Implementation Contracts
      • v3.x.x
      • v2.x.x
      • v1.x.x
    • Smart Contract Documentation
    • Common Features
      • Access Control
      • Royalties
      • Story Inscriptions
      • NFT Delegation
      • BlockList
    • ERC721TL
    • ERC1155TL
    • Shatter
    • ERC7160TL
    • Doppelganger
    • Collector's Choice
  • ⛔BlockList
    • BlockList Overview
    • Implementation Contracts
    • Deployments
    • Smart Contract Documentation
  • 🔖Story Inscriptions
    • Story Inscriptions Overview
    • Story Inscription Format
    • Smart Contract Documentation
  • 🖼️T.R.A.C.E.
    • T.R.A.C.E. Overview
    • Record Schema
    • Implementation
    • Smart Contract Documentation
      • TRACE
      • TRACERSRegistry
  • 🥞Stacks
    • Stacks Overview
    • Deployments
    • Smart Contract Documentation
  • 🎨Dynamic Art
    • Dynamic Art Overview
    • How to Create
    • How to Display
  • 🔗Integrations
    • Deploying TL Contracts
    • NFT Delegation
    • Integrating with Marketplaces
    • Metadata Structure
    • Inheriting TL Contracts
    • Onchain Art
    • Embeddable Components
  • ❔Miscellaneous
    • Supported Blockchains
    • tl-sol-tools
    • Licensing
    • Batch Upload Secret JSON
Powered by GitBook
On this page
  • Ownable Access Control
  • Roles
  1. TL Creator Contracts
  2. Common Features

Access Control

TL Creator Contracts implement access control mechanisms to allow for ease of use while promoting security.

PreviousCommon FeaturesNextRoyalties

Last updated 1 year ago

Ownable Access Control

We have combined OpenZeppelin's Ownable contract with role based access mechanisms to make a simple, effective ownership model.

Roles

TL Creator Contracts have two roles defined

ADMIN_ROLE

This role is able to mint tokens, add APPROVED_MINT_CONTRACT roles, propose/push token metadata updates, and add creator stories on behalf of the contract owner.

APPROVED_MINT_CONTRACT

This role is allowed to mint using the externalMint functions in ERC721TL and ERC1155TL.

📑
Page cover image
https://github.com/Transient-Labs/tl-sol-tools/blob/main/src/access/OwnableAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin-contracts-5.0.2/access/Ownable.sol";
import {EnumerableSet} from "@openzeppelin-contracts-5.0.2/utils/structs/EnumerableSet.sol";

/// @title OwnableAccessControl.sol
/// @notice Single owner, flexible access control mechanics
/// @dev Can easily be extended by inheriting and applying additional roles
/// @dev By default, only the owner can grant roles but by inheriting, but you
///      may allow other roles to grant roles by using the internal helper.
/// @author transientlabs.xyz
/// @custom:version 3.0.0
abstract contract OwnableAccessControl is Ownable {
    /*//////////////////////////////////////////////////////////////////////////
                                State Variables
    //////////////////////////////////////////////////////////////////////////*/

    using EnumerableSet for EnumerableSet.AddressSet;

    uint256 private _c; // counter to be able to revoke all priviledges
    mapping(uint256 => mapping(bytes32 => mapping(address => bool))) private _roleStatus;
    mapping(uint256 => mapping(bytes32 => EnumerableSet.AddressSet)) private _roleMembers;

    /*//////////////////////////////////////////////////////////////////////////
                                    Events
    //////////////////////////////////////////////////////////////////////////*/

    /// @param from Address that authorized the role change
    /// @param user The address who's role has been changed
    /// @param approved Boolean indicating the user's status in role
    /// @param role The bytes32 role created in the inheriting contract
    event RoleChange(address indexed from, address indexed user, bool indexed approved, bytes32 role);

    /// @param from Address that authorized the revoke
    event AllRolesRevoked(address indexed from);

    /*//////////////////////////////////////////////////////////////////////////
                                    Errors
    //////////////////////////////////////////////////////////////////////////*/

    /// @dev Does not have specified role
    error NotSpecifiedRole(bytes32 role);

    /// @dev Is not specified role or owner
    error NotRoleOrOwner(bytes32 role);

    /*//////////////////////////////////////////////////////////////////////////
                                    Modifiers
    //////////////////////////////////////////////////////////////////////////*/

    modifier onlyRole(bytes32 role) {
        if (!hasRole(role, msg.sender)) {
            revert NotSpecifiedRole(role);
        }
        _;
    }

    modifier onlyRoleOrOwner(bytes32 role) {
        if (!hasRole(role, msg.sender) && owner() != msg.sender) {
            revert NotRoleOrOwner(role);
        }
        _;
    }

    /*//////////////////////////////////////////////////////////////////////////
                                Constructor
    //////////////////////////////////////////////////////////////////////////*/

    constructor() Ownable(msg.sender) {}

    /*//////////////////////////////////////////////////////////////////////////
                            External Role Functions
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Function to revoke all roles currently present
    /// @dev Increments the `_c` variables
    /// @dev Requires owner privileges
    function revokeAllRoles() external onlyOwner {
        _c++;
        emit AllRolesRevoked(msg.sender);
    }

    /// @notice Function to renounce role
    /// @param role Bytes32 role created in inheriting contracts
    function renounceRole(bytes32 role) external {
        address[] memory members = new address[](1);
        members[0] = msg.sender;
        _setRole(role, members, false);
    }

    /// @notice Function to grant/revoke a role to an address
    /// @dev Requires owner to call this function but this may be further
    ///      extended using the internal helper function in inheriting contracts
    /// @param role Bytes32 role created in inheriting contracts
    /// @param roleMembers List of addresses that should have roles attached to them based on `status`
    /// @param status Bool whether to remove or add `roleMembers` to the `role`
    function setRole(bytes32 role, address[] memory roleMembers, bool status) external onlyOwner {
        _setRole(role, roleMembers, status);
    }

    /*//////////////////////////////////////////////////////////////////////////
                                External View Functions
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Function to see if an address is the owner
    /// @param role Bytes32 role created in inheriting contracts
    /// @param potentialRoleMember Address to check for role membership
    function hasRole(bytes32 role, address potentialRoleMember) public view returns (bool) {
        return _roleStatus[_c][role][potentialRoleMember];
    }

    /// @notice Function to get role members
    /// @param role Bytes32 role created in inheriting contracts
    function getRoleMembers(bytes32 role) public view returns (address[] memory) {
        return _roleMembers[_c][role].values();
    }

    /*//////////////////////////////////////////////////////////////////////////
                                Internal Helper Functions
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Helper function to set addresses for a role
    /// @param role Bytes32 role created in inheriting contracts
    /// @param roleMembers List of addresses that should have roles attached to them based on `status`
    /// @param status Bool whether to remove or add `roleMembers` to the `role`
    function _setRole(bytes32 role, address[] memory roleMembers, bool status) internal {
        for (uint256 i = 0; i < roleMembers.length; i++) {
            _roleStatus[_c][role][roleMembers[i]] = status;
            if (status) {
                _roleMembers[_c][role].add(roleMembers[i]);
            } else {
                _roleMembers[_c][role].remove(roleMembers[i]);
            }
            emit RoleChange(msg.sender, roleMembers[i], status, role);
        }
    }
}