Contract Overview
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Contract Name:
NFTFactory
Compiler Version
v0.8.15+commit.e14f2714
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @title Mintable ERC721 * * @notice Defines mint capabilities for Alethea ERC721 tokens. * This interface should be treated as a definition of what mintable means for ERC721 */ interface MintableERC721 { /** * @notice Checks if specified token exists * * @dev Returns whether the specified token ID has an ownership * information associated with it * * @param _tokenId ID of the token to query existence for * @return whether the token exists (true - exists, false - doesn't exist) */ function exists(uint256 _tokenId) external view returns(bool); /** * @dev Creates new token with token ID specified * and assigns an ownership `_to` for this token * * @dev Unsafe: doesn't execute `onERC721Received` on the receiver. * Prefer the use of `saveMint` instead of `mint`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint token to * @param _tokenId ID of the token to mint */ function mint(address _to, uint256 _tokenId) external; /** * @dev Creates new tokens starting with token ID specified * and assigns an ownership `_to` for these tokens * * @dev Token IDs to be minted: [_tokenId, _tokenId + n) * * @dev n must be greater or equal 2: `n > 1` * * @dev Unsafe: doesn't execute `onERC721Received` on the receiver. * Prefer the use of `saveMintBatch` instead of `mintBatch`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint tokens to * @param _tokenId ID of the first token to mint * @param n how many tokens to mint, sequentially increasing the _tokenId */ function mintBatch(address _to, uint256 _tokenId, uint256 n) external; /** * @dev Creates new token with token ID specified * and assigns an ownership `_to` for this token * * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls * `onERC721Received` on `_to` and throws if the return value is not * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint token to * @param _tokenId ID of the token to mint */ function safeMint(address _to, uint256 _tokenId) external; /** * @dev Creates new token with token ID specified * and assigns an ownership `_to` for this token * * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls * `onERC721Received` on `_to` and throws if the return value is not * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint token to * @param _tokenId ID of the token to mint * @param _data additional data with no specified format, sent in call to `_to` */ function safeMint(address _to, uint256 _tokenId, bytes memory _data) external; /** * @dev Creates new tokens starting with token ID specified * and assigns an ownership `_to` for these tokens * * @dev Token IDs to be minted: [_tokenId, _tokenId + n) * * @dev n must be greater or equal 2: `n > 1` * * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls * `onERC721Received` on `_to` and throws if the return value is not * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint token to * @param _tokenId ID of the token to mint * @param n how many tokens to mint, sequentially increasing the _tokenId */ function safeMintBatch(address _to, uint256 _tokenId, uint256 n) external; /** * @dev Creates new tokens starting with token ID specified * and assigns an ownership `_to` for these tokens * * @dev Token IDs to be minted: [_tokenId, _tokenId + n) * * @dev n must be greater or equal 2: `n > 1` * * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls * `onERC721Received` on `_to` and throws if the return value is not * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. * * @dev Should have a restricted access handled by the implementation * * @param _to an address to mint token to * @param _tokenId ID of the token to mint * @param n how many tokens to mint, sequentially increasing the _tokenId * @param _data additional data with no specified format, sent in call to `_to` */ function safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) external; } /** * @title Alethea Burnable ERC721 * * @notice Defines burn capabilities for Alethea ERC721 tokens. * This interface should be treated as a definition of what burnable means for ERC721 */ interface BurnableERC721 { /** * @notice Destroys the token with token ID specified * * @dev Should be accessible publicly by token owners. * May have a restricted access handled by the implementation * * @param _tokenId ID of the token to burn */ function burn(uint256 _tokenId) external; } /** * @title With Base URI * * @notice A marker interface for the contracts having the baseURI() function * or public string variable named baseURI * NFT implementations like TinyERC721, or ShortERC721 are example of such smart contracts */ interface WithBaseURI { /** * @dev Usually used in NFT implementations to construct ERC721Metadata.tokenURI as * `base URI + token ID` if token URI is not set (not present in `_tokenURIs` mapping) * * @dev For example, if base URI is https://api.com/token/, then token #1 * will have an URI https://api.com/token/1 */ function baseURI() external view returns(string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. * * @dev Copy of the Zeppelin's library: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b0cf6fbb7a70f31527f36579ad644e1cf12fdf4e/contracts/utils/cryptography/ECDSA.sol */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } } else if (signature.length == 64) { // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { let vs := mload(add(signature, 0x40)) r := mload(add(signature, 0x20)) s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } } else { revert("invalid signature length"); } return recover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require( uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "invalid signature 's' value" ); require(v == 27 || v == 28, "invalid signature 'v' value"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "../interfaces/ERC721SpecExt.sol"; import "../utils/AccessControl.sol"; import "../lib/ECDSA.sol"; /** * @title Alethea NFT Factory * * @notice NFT Factory is a helper smart contract responsible for minting arbitrary NFTs * * @notice It supports two mechanisms: * - minting delegation: authorized address executes mint function on the helper, * and helper executes minting function on the target ERC721 contract as an internal transaction * - meta transaction minting or minting with an authorization: authorized address signs * the minting authorization message and any address executes mint function on the helper * * @notice Second mechanism allows to shift the gas costs for the transaction to any address * (usually this is the NFT beneficiary - an address which receives an NFT) * * @dev The signature is constructed via EIP-712 similar to EIP-2612, or EIP-3009 * * @dev Target ERC721 contract(s) must allow helper to mint the tokens, this should be configured * as part of the deployment or setup processes */ contract NFTFactory is AccessControl { /** * @dev A record of used nonces for EIP-712 transactions * * @dev A record of used nonces for signing/validating signatures * in `mintWithAuthorization` for every mint * * @dev Maps authorizer address => nonce => true/false (used unused) */ mapping(address => mapping(bytes32 => bool)) private usedNonces; /** * @notice EIP-712 contract's domain separator, * see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator */ bytes32 public immutable DOMAIN_SEPARATOR; /** * @notice EIP-712 contract's domain typeHash, * see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash * * @dev Note: we do not include version into the domain typehash/separator, * it is implied version is concatenated to the name field, like "NFTFactoryV2" */ // keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)") bytes32 public constant DOMAIN_TYPEHASH = 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866; /** * @notice EIP-712 MintWithAuthorization struct typeHash, * see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash */ // keccak256("MintWithAuthorization(address contract,address to,uint256 tokenId,uint256 validAfter,uint256 validBefore,bytes32 nonce)") bytes32 public constant MINT_WITH_AUTHORIZATION_TYPEHASH = 0x495835d970a03ff092657fca9abde67d34a0bb73a0bba258a5fa90c4ce4340f6; /** * @notice EIP-712 CancelAuthorization struct typeHash, * see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash */ // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; /** * @notice Enables meta transaction minting (minting with an authorization * via an EIP712 signature) * @dev Feature FEATURE_MINTING_WITH_AUTH must be enabled in order for * `mintWithAuthorization()` function to succeed */ uint32 public constant FEATURE_MINTING_WITH_AUTH = 0x0000_0001; /** * @notice Factory minter is responsible for creating (minting) * tokens to an arbitrary address * @dev Role ROLE_FACTORY_MINTER allows minting tokens * (executing `mint` function) */ uint32 public constant ROLE_FACTORY_MINTER = 0x0001_0000; /** * @dev Fired in mint() and mintWithAuthorization() after an NFT is minted * * @param erc721Address ERC721 contract address which was minted * @param to an address NFT was minted to * @param tokenId NFT ID which was minted */ event Minted(address indexed erc721Address, address indexed to, uint256 indexed tokenId); /** * @dev Fired whenever the nonce gets used (ex.: `transferWithAuthorization`, `receiveWithAuthorization`) * * @param authorizer an address which has used the nonce * @param nonce the nonce used */ event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); /** * @dev Fired whenever the nonce gets cancelled (ex.: `cancelAuthorization`) * * @dev Both `AuthorizationUsed` and `AuthorizationCanceled` imply the nonce * cannot be longer used, the only difference is that `AuthorizationCanceled` * implies no smart contract state change made (except the nonce marked as cancelled) * * @param authorizer an address which has cancelled the nonce * @param nonce the nonce cancelled */ event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce); /** * Deploys the helper contract and initializes DOMAIN_SEPARATOR using the created smart contract address */ constructor() { // build the EIP-712 contract domain separator, see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator // note: we specify contract version in its name DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("NFTFactoryV1")), block.chainid, address(this))); } /** * @notice Restricted access function to mint an NFT * * @dev Doesn't allow minting the token with ID zero * @dev Requires an executor to have ROLE_MINTER permission * @dev Requires target ERC721 contract to be mintable (`MintableERC721`) * @dev Requires target ERC721 contract instance to allow minting via helper * * @param _mintableErc721 target ERC721 contract instance to mint token on, * compatible with `MintableERC721` * @param _to an address to mint token to * @param _tokenId target ERC721 token ID to mint */ function mint(address _mintableErc721, address _to, uint256 _tokenId) public { // delegate to _mint() __mint(msg.sender, _mintableErc721, _to, _tokenId); } /** * @dev Auxiliary internally used function to mint an NFT * * @dev Unsafe: doesn't verify real tx executor (msg.sender) permissions, but the permissions of * the address specified as an executor, must be kept private at all times * * @dev Doesn't allow minting the token with ID zero * @dev Requires an executor to have ROLE_MINTER permission * @dev Requires target ERC721 contract to be mintable (`MintableERC721`) * @dev Requires target ERC721 contract instance to allow minting via helper * * @param _executor an address on which behalf the operation is executed, * this is usually `msg.sender` but this can be different address for * the EIP-712 like transactions (mint with authorization) * @param _mintableErc721 target ERC721 contract instance to mint token on, * compatible with `MintableERC721` * @param _to an address to mint token to * @param _tokenId target ERC721 token ID to mint */ function __mint(address _executor, address _mintableErc721, address _to, uint256 _tokenId) private { // verify the access permission require(isOperatorInRole(_executor, ROLE_FACTORY_MINTER), "access denied"); // verify the inputs require(_mintableErc721 != address(0), "ERC721 instance addr is not set"); require(_to != address(0), "NFT receiver addr is not set"); require(_tokenId != 0, "token ID is not set"); // delegate to the target ERC721 contract MintableERC721(_mintableErc721).safeMint(_to, _tokenId); // emit an event emit Minted(_mintableErc721, _to, _tokenId); } /** * @notice Executes a mint function with a signed authorization * * @param _mintableErc721 target ERC721 contract instance to mint token on, * compatible with `MintableERC721` * @param _to an address to mint token to * @param _tokenId target ERC721 token ID to mint * @param _validAfter signature valid after time (unix timestamp) * @param _validBefore signature valid before time (unix timestamp) * @param _nonce unique random nonce * @param v the recovery byte of the signature * @param r half of the ECDSA signature pair * @param s half of the ECDSA signature pair */ function mintWithAuthorization( address _mintableErc721, address _to, uint256 _tokenId, uint256 _validAfter, uint256 _validBefore, bytes32 _nonce, uint8 v, bytes32 r, bytes32 s ) public { // ensure EIP-712 minting with authorization is enabled require(isFeatureEnabled(FEATURE_MINTING_WITH_AUTH), "minting with auth is disabled"); // derive signer of the EIP712 MintWithAuthorization message address signer = __deriveSigner( abi.encode(MINT_WITH_AUTHORIZATION_TYPEHASH, _mintableErc721, _to, _tokenId, _validAfter, _validBefore, _nonce), v, r, s ); // perform message integrity and security validations require(block.timestamp > _validAfter, "signature not yet valid"); require(block.timestamp < _validBefore, "signature expired"); // use the nonce supplied (verify, mark as used, emit event) __useNonce(signer, _nonce, false); // delegate call to `_mint` - execute the logic required __mint(signer, _mintableErc721, _to, _tokenId); } /** * @notice Returns the state of an authorization, more specifically * if the specified nonce was already used by the address specified * * @dev Nonces are expected to be client-side randomly generated 32-byte data * unique to the authorizer's address * * @param _authorizer Authorizer's address * @param _nonce Nonce of the authorization * @return true if the nonce is used */ function authorizationState( address _authorizer, bytes32 _nonce ) external view returns (bool) { // simply return the value from the mapping return usedNonces[_authorizer][_nonce]; } /** * @notice Cancels the authorization (using EIP-712 signature) * * @param _authorizer transaction authorizer * @param _nonce unique random nonce to cancel (mark as used) * @param v the recovery byte of the signature * @param r half of the ECDSA signature pair * @param s half of the ECDSA signature pair */ function cancelAuthorization( address _authorizer, bytes32 _nonce, uint8 v, bytes32 r, bytes32 s ) public { // derive signer of the EIP712 ReceiveWithAuthorization message address signer = __deriveSigner(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, _authorizer, _nonce), v, r, s); // perform message integrity and security validations require(signer == _authorizer, "invalid signature"); // cancel the nonce supplied (verify, mark as used, emit event) __useNonce(_authorizer, _nonce, true); } /** * @notice Cancels the authorization * * @param _nonce unique random nonce to cancel (mark as used) */ function cancelAuthorization(bytes32 _nonce) public { // cancel the nonce supplied (verify, mark as used, emit event) __useNonce(msg.sender, _nonce, true); } /** * @dev Auxiliary function to verify structured EIP712 message signature and derive its signer * * @param abiEncodedTypehash abi.encode of the message typehash together with all its parameters * @param v the recovery byte of the signature * @param r half of the ECDSA signature pair * @param s half of the ECDSA signature pair */ function __deriveSigner(bytes memory abiEncodedTypehash, uint8 v, bytes32 r, bytes32 s) private view returns(address) { // build the EIP-712 hashStruct of the message bytes32 hashStruct = keccak256(abiEncodedTypehash); // calculate the EIP-712 digest "\x19\x01" ‖ domainSeparator ‖ hashStruct(message) bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); // recover the address which signed the message with v, r, s address signer = ECDSA.recover(digest, v, r, s); // return the signer address derived from the signature return signer; } /** * @dev Auxiliary function to use/cancel the nonce supplied for a given authorizer: * 1. Verifies the nonce was not used before * 2. Marks the nonce as used * 3. Emits an event that the nonce was used/cancelled * * @dev Set `_cancellation` to false (default) to use nonce, * set `_cancellation` to true to cancel nonce * * @dev It is expected that the nonce supplied is a randomly * generated uint256 generated by the client * * @param _authorizer an address to use/cancel nonce for * @param _nonce random nonce to use * @param _cancellation true to emit `AuthorizationCancelled`, false to emit `AuthorizationUsed` event */ function __useNonce(address _authorizer, bytes32 _nonce, bool _cancellation) private { // verify nonce was not used before require(!usedNonces[_authorizer][_nonce], "invalid nonce"); // update the nonce state to "used" for that particular signer to avoid replay attack usedNonces[_authorizer][_nonce] = true; // depending on the usage type (use/cancel) if(_cancellation) { // emit an event regarding the nonce cancelled emit AuthorizationCanceled(_authorizer, _nonce); } else { // emit an event regarding the nonce used emit AuthorizationUsed(_authorizer, _nonce); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @title Access Control List * * @notice Access control smart contract provides an API to check * if specific operation is permitted globally and/or * if particular user has a permission to execute it. * * @notice It deals with two main entities: features and roles. * * @notice Features are designed to be used to enable/disable specific * functions (public functions) of the smart contract for everyone. * @notice User roles are designed to restrict access to specific * functions (restricted functions) of the smart contract to some users. * * @notice Terms "role", "permissions" and "set of permissions" have equal meaning * in the documentation text and may be used interchangeably. * @notice Terms "permission", "single permission" implies only one permission bit set. * * @notice Access manager is a special role which allows to grant/revoke other roles. * Access managers can only grant/revoke permissions which they have themselves. * As an example, access manager with no other roles set can only grant/revoke its own * access manager permission and nothing else. * * @notice Access manager permission should be treated carefully, as a super admin permission: * Access manager with even no other permission can interfere with another account by * granting own access manager permission to it and effectively creating more powerful * permission set than its own. * * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API * to check/know "who is allowed to do this thing". * @dev Zeppelin implementation is more flexible: * - it allows setting unlimited number of roles, while current is limited to 256 different roles * - it allows setting an admin for each role, while current allows having only one global admin * @dev Current implementation is more lightweight: * - it uses only 1 bit per role, while Zeppelin uses 256 bits * - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows * setting only one role in a single transaction * * @dev This smart contract is designed to be inherited by other * smart contracts which require access control management capabilities. * * @dev Access manager permission has a bit 255 set. * This bit must not be used by inheriting contracts for any other permissions/features. */ contract AccessControl { /** * @notice Access manager is responsible for assigning the roles to users, * enabling/disabling global features of the smart contract * @notice Access manager can add, remove and update user roles, * remove and update global features * * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled */ uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000; /** * @dev Bitmask representing all the possible permissions (super admin role) * @dev Has all the bits are enabled (2^256 - 1 value) */ uint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF... /** * @notice Privileged addresses with defined roles/permissions * @notice In the context of ERC20/ERC721 tokens these can be permissions to * allow minting or burning tokens, transferring on behalf and so on * * @dev Maps user address to the permissions bitmask (role), where each bit * represents a permission * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF * represents all possible permissions * @dev 'This' address mapping represents global features of the smart contract */ mapping(address => uint256) public userRoles; /** * @dev Fired in updateRole() and updateFeatures() * * @param _by operator which called the function * @param _to address which was granted/revoked permissions * @param _requested permissions requested * @param _actual permissions effectively set */ event RoleUpdated(address indexed _by, address indexed _to, uint256 _requested, uint256 _actual); /** * @notice Creates an access control instance, * setting contract creator to have full privileges */ constructor() { // contract creator has full privileges userRoles[msg.sender] = FULL_PRIVILEGES_MASK; } /** * @notice Retrieves globally set of features enabled * * @dev Effectively reads userRoles role for the contract itself * * @return 256-bit bitmask of the features enabled */ function features() public view returns(uint256) { // features are stored in 'this' address mapping of `userRoles` structure return userRoles[address(this)]; } /** * @notice Updates set of the globally enabled features (`features`), * taking into account sender's permissions * * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission * @dev Function is left for backward compatibility with older versions * * @param _mask bitmask representing a set of features to enable/disable */ function updateFeatures(uint256 _mask) public { // delegate call to `updateRole` updateRole(address(this), _mask); } /** * @notice Updates set of permissions (role) for a given user, * taking into account sender's permissions. * * @dev Setting role to zero is equivalent to removing an all permissions * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to * copying senders' permissions (role) to the user * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission * * @param operator address of a user to alter permissions for or zero * to alter global features of the smart contract * @param role bitmask representing a set of permissions to * enable/disable for a user specified */ function updateRole(address operator, uint256 role) public { // caller must have a permission to update user roles require(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied"); // evaluate the role and reassign it userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role); // fire an event emit RoleUpdated(msg.sender, operator, role, userRoles[operator]); } /** * @notice Determines the permission bitmask an operator can set on the * target permission set * @notice Used to calculate the permission bitmask to be set when requested * in `updateRole` and `updateFeatures` functions * * @dev Calculated based on: * 1) operator's own permission set read from userRoles[operator] * 2) target permission set - what is already set on the target * 3) desired permission set - what do we want set target to * * @dev Corner cases: * 1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`: * `desired` bitset is returned regardless of the `target` permission set value * (what operator sets is what they get) * 2) Operator with no permissions (zero bitset): * `target` bitset is returned regardless of the `desired` value * (operator has no authority and cannot modify anything) * * @dev Example: * Consider an operator with the permissions bitmask 00001111 * is about to modify the target permission set 01010101 * Operator wants to set that permission set to 00110011 * Based on their role, an operator has the permissions * to update only lowest 4 bits on the target, meaning that * high 4 bits of the target set in this example is left * unchanged and low 4 bits get changed as desired: 01010011 * * @param operator address of the contract operator which is about to set the permissions * @param target input set of permissions to operator is going to modify * @param desired desired set of permissions operator would like to set * @return resulting set of permissions given operator will set */ function evaluateBy(address operator, uint256 target, uint256 desired) public view returns(uint256) { // read operator's permissions uint256 p = userRoles[operator]; // taking into account operator's permissions, // 1) enable the permissions desired on the `target` target |= p & desired; // 2) disable the permissions desired on the `target` target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired)); // return calculated result return target; } /** * @notice Checks if requested set of features is enabled globally on the contract * * @param required set of features to check against * @return true if all the features requested are enabled, false otherwise */ function isFeatureEnabled(uint256 required) public view returns(bool) { // delegate call to `__hasRole`, passing `features` property return __hasRole(features(), required); } /** * @notice Checks if transaction sender `msg.sender` has all the permissions required * * @param required set of permissions (role) to check against * @return true if all the permissions requested are enabled, false otherwise */ function isSenderInRole(uint256 required) public view returns(bool) { // delegate call to `isOperatorInRole`, passing transaction sender return isOperatorInRole(msg.sender, required); } /** * @notice Checks if operator has all the permissions (role) required * * @param operator address of the user to check role for * @param required set of permissions (role) to check * @return true if all the permissions requested are enabled, false otherwise */ function isOperatorInRole(address operator, uint256 required) public view returns(bool) { // delegate call to `__hasRole`, passing operator's permissions (role) return __hasRole(userRoles[operator], required); } /** * @dev Checks if role `actual` contains all the permissions required `required` * * @param actual existent role * @param required required role * @return true if actual has required role (all permissions), false otherwise */ function __hasRole(uint256 actual, uint256 required) internal pure returns(bool) { // check the bitmask for the role required and return the result return actual & required == required; } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc721Address","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actual","type":"uint256"}],"name":"RoleUpdated","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_MINTING_WITH_AUTH","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_ACCESS_MANAGER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_FACTORY_MINTER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_authorizer","type":"address"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_authorizer","type":"address"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_nonce","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"target","type":"uint256"},{"internalType":"uint256","name":"desired","type":"uint256"}],"name":"evaluateBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"features","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isFeatureEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isOperatorInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isSenderInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_mintableErc721","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mintableErc721","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_validAfter","type":"uint256"},{"internalType":"uint256","name":"_validBefore","type":"uint256"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"mintWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mask","type":"uint256"}],"name":"updateFeatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"role","type":"uint256"}],"name":"updateRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRoles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b503360009081526020818152604091829020600019905581518083018352600c81526b4e4654466163746f7279563160a01b9082015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866918101919091527f06df569e81a0cee0d4bf93bcbb32cd170bf15a994570fea8a3c8e631cc4b61719181019190915246606082015230608082015260a00160408051601f198184030181529190528051602090910120608052608051610e056100e460003960008181610185015261072a0152610e056000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c8063c688d693116100ad578063d916948711610071578063d9169487146102a3578063e94a0102146102ca578063f822d5aa14610303578063fcc2c07814610316578063ff2642cf1461032957600080fd5b8063c688d6931461024d578063c6c3bbe614610260578063ce8b3a5814610273578063d1333c981461027d578063d5bb7f671461029057600080fd5b806374d5e100116100f457806374d5e100146101df578063845ab94f146101ff578063ae5b102e14610212578063ae682e2e14610225578063c4d029c61461023057600080fd5b806320606b70146101315780632b5214161461016b5780633644e515146101805780635a049a70146101a7578063725f3626146101bc575b600080fd5b6101587f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b6040519081526020015b60405180910390f35b30600090815260208190526040902054610158565b6101587f000000000000000000000000000000000000000000000000000000000000000081565b6101ba6101b5366004610c36565b610350565b005b6101cf6101ca366004610c84565b610424565b6040519015158152602001610162565b6101586101ed366004610c9d565b60006020819052908152604090205481565b6101ba61020d366004610c84565b61043f565b6101ba610220366004610cb8565b61044e565b610158600160ff1b81565b610238600181565b60405163ffffffff9091168152602001610162565b6101cf61025b366004610cb8565b610518565b6101ba61026e366004610ce2565b61053d565b6102386201000081565b6101ba61028b366004610d1e565b61054e565b6101ba61029e366004610c84565b6106cc565b6101587f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6101cf6102d8366004610cb8565b6001600160a01b03919091166000908152600160209081526040808320938352929052205460ff1690565b610158610311366004610d9c565b6106d6565b6101cf610324366004610c84565b610701565b6101587f495835d970a03ff092657fca9abde67d34a0bb73a0bba258a5fa90c4ce4340f681565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03871691810191909152606081018590526000906103b4906080015b60405160208183030381529060405285858561070d565b9050856001600160a01b0316816001600160a01b0316146104105760405162461bcd60e51b8152602060048201526011602482015270696e76616c6964207369676e617475726560781b60448201526064015b60405180910390fd5b61041c8686600161078f565b505050505050565b30600090815260208190526040812054821682145b92915050565b61044b3382600161078f565b50565b61045b600160ff1b610701565b6104975760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610407565b6001600160a01b0382166000908152602081905260409020546104bc903390836106d6565b6001600160a01b03831660008181526020818152604091829020849055815185815290810193909352909133917f5a10526456f5116c0b7b80582c217d666243fd51b6a2d92c8011e601c2462e5f910160405180910390a35050565b6001600160a01b038216600090815260208190526040812054821682145b9392505050565b6105493384848461089e565b505050565b6105586001610424565b6105a45760405162461bcd60e51b815260206004820152601d60248201527f6d696e74696e67207769746820617574682069732064697361626c65640000006044820152606401610407565b604080517f495835d970a03ff092657fca9abde67d34a0bb73a0bba258a5fa90c4ce4340f660208201526001600160a01b03808c169282019290925290891660608201526080810188905260a0810187905260c0810186905260e08101859052600090610614906101000161039d565b90508642116106655760405162461bcd60e51b815260206004820152601760248201527f7369676e6174757265206e6f74207965742076616c69640000000000000000006044820152606401610407565b8542106106a85760405162461bcd60e51b81526020600482015260116024820152701cda59db985d1d5c9948195e1c1a5c9959607a1b6044820152606401610407565b6106b48186600061078f565b6106c0818b8b8b61089e565b50505050505050505050565b61044b308261044e565b6001600160a01b03929092166000908152602081905260409020546000198084188216189216171690565b60006104393383610518565b835160208086019190912060405161190160f01b928101929092527f00000000000000000000000000000000000000000000000000000000000000006022830152604282018190526000918290606201604051602081830303815290604052805190602001209050600061078382888888610a82565b98975050505050505050565b6001600160a01b038316600090815260016020908152604080832085845290915290205460ff16156107f35760405162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b6044820152606401610407565b6001600160a01b0383166000908152600160208181526040808420868552909152909120805460ff1916909117905580156108635760405182906001600160a01b038516907f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8190600090a3505050565b60405182906001600160a01b038516907f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a590600090a3505050565b6108ab8462010000610518565b6108e75760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610407565b6001600160a01b03831661093d5760405162461bcd60e51b815260206004820152601f60248201527f45524337323120696e7374616e63652061646472206973206e6f7420736574006044820152606401610407565b6001600160a01b0382166109935760405162461bcd60e51b815260206004820152601c60248201527f4e46542072656365697665722061646472206973206e6f7420736574000000006044820152606401610407565b806000036109d95760405162461bcd60e51b81526020600482015260136024820152721d1bdad95b881251081a5cc81b9bdd081cd95d606a1b6044820152606401610407565b604051632851206560e21b81526001600160a01b0383811660048301526024820183905284169063a144819490604401600060405180830381600087803b158015610a2357600080fd5b505af1158015610a37573d6000803e3d6000fd5b5050505080826001600160a01b0316846001600160a01b03167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f060405160405180910390a450505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115610af45760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202773272076616c756500000000006044820152606401610407565b8360ff16601b1480610b0957508360ff16601c145b610b555760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202776272076616c756500000000006044820152606401610407565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015610ba9573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610c005760405162461bcd60e51b8152602060048201526011602482015270696e76616c6964207369676e617475726560781b6044820152606401610407565b95945050505050565b80356001600160a01b0381168114610c2057600080fd5b919050565b803560ff81168114610c2057600080fd5b600080600080600060a08688031215610c4e57600080fd5b610c5786610c09565b945060208601359350610c6c60408701610c25565b94979396509394606081013594506080013592915050565b600060208284031215610c9657600080fd5b5035919050565b600060208284031215610caf57600080fd5b61053682610c09565b60008060408385031215610ccb57600080fd5b610cd483610c09565b946020939093013593505050565b600080600060608486031215610cf757600080fd5b610d0084610c09565b9250610d0e60208501610c09565b9150604084013590509250925092565b60008060008060008060008060006101208a8c031215610d3d57600080fd5b610d468a610c09565b9850610d5460208b01610c09565b975060408a0135965060608a0135955060808a0135945060a08a01359350610d7e60c08b01610c25565b925060e08a013591506101008a013590509295985092959850929598565b600080600060608486031215610db157600080fd5b610dba84610c09565b9560208501359550604090940135939250505056fea26469706673582212205da09b76b316b383871649cf1f936304792d07632f756f85f38663aee6f3e6aa64736f6c634300080f0033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.