Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 9 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18835912 | 260 days ago | 0.09 ETH | ||||
18835912 | 260 days ago | 0.01 ETH | ||||
18677002 | 282 days ago | 0.09 ETH | ||||
18677002 | 282 days ago | 0.01 ETH | ||||
18674541 | 283 days ago | 0.09 ETH | ||||
18674541 | 283 days ago | 0.01 ETH | ||||
18663427 | 284 days ago | 0.09 ETH | ||||
18663427 | 284 days ago | 0.01 ETH | ||||
18660662 | 285 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x86a30746d6e4b3e96be8364f162eb1e146ebbe4c
Contract Name:
ERC1155Minter
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../../access/AccessControl.sol"; import "../common/ERC2981PersonalEditions.sol"; import "./extensions/ERC1155Burnable.sol"; import "./extensions/ERC1155URIStorage.sol"; import "./extensions/ERC1155Supply.sol"; /************************************************************* * @title ERC1155Minter * * * * @notice Self-sovreign ERC-1155 minter preset * * * * @dev {ERC1155} token featuring: * * + minting and lazy minting * * + ERC-2981 royalty standard tracking both royalties * * and token market (primary and secondary) * * * * @custom:security-contact [email protected] * ************************************************************/ contract ERC1155Minter is AccessControl, ERC2981PersonalEditions, ERC1155Burnable, ERC1155URIStorage, ERC1155Supply { // EIP-712 bytes32 private DOMAIN_SEPARATOR; bytes32 private immutable VOUCHER_TYPEHASH; // Acces Control bytes32 internal constant MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6; // keccak256("MINTER_ROLE"); one or more smart contracts allowed to call the mint function, eg. the Marketplace contract address private immutable _FACTORY; // Metadata (OPTIONAL non-standard, see https://eips.ethereum.org/EIPS/eip-1155#metadata-choices) string public name; string public symbol; /** * @param totalValue maximum tokenId editions cap, if unlimited supply use `type(uint256).max` i.e. 2**256 - 1 * @param endTime timestamp for when the signed voucher should expire, * if no expiration is needed, timestamp should be `type(uint256).max` i.e. 2**256 - 1, * or anything above 2^32, i.e. 4294967296, i.e. voucher expires after 2106 (in 83 years time) */ struct Voucher { bytes32 tokenUri; uint256 unitPrice; uint256 totalValue; uint256 endTime; uint256 saleCommissionBps; uint256 marketFeeBps; address saleCommissionRecipient; address marketFeeRecipient; } /*----------------------------------------------------------*| |* # MINTER FUNCTIONS *| |*----------------------------------------------------------*/ /** * @param _tokenId may correspond to an already existing tokenId, if so, the corresponding tokenUri at `_tokenURIs[_tokenId]` MUST equal `voucher.tokenUri`, * or it may be an inexisting tokenId such as 2^256-1 in which case a new token will be minted, provided that `voucher.tokenUri` has never been minted before. * @param _value the amount/supply of `tokenId` to be minted, provided that the total tokenId's circulating supply does not exceed the one (if any) specified in `voucher.totalValue`. * @param _to buyer, needed if using a external payment gateway, so that the minted tokenId value is sent to the address specified insead of `msg.sender` * @param _data data bytes are passed to `onErc1155Received` function if the `_to` address is a contract, for example a marketplace. * `onErc1155Received` is not being called on the minter's address when a new tokenId is minted however, even if it was contract. */ function lazyMint( Voucher calldata voucher, uint256 _tokenId, uint256 _value, address _to, bytes memory _signature, bytes calldata _data ) external payable { /*----------------------------------------------------------*| |* # EIP-712 TYPED DATA SIGNATURE VERIFICATION *| |*----------------------------------------------------------*/ address signer = _recover(voucher, _signature); uint256 sellerAmount = voucher.unitPrice * _value; require( // `msg.value` MUST equal ETH unit price multiplied by token value/amount msg.value == sellerAmount && // voucher MUST not be expired block.timestamp < voucher.endTime && // signer MUST have MINTER_ROLE hasRole(MINTER_ROLE, signer) ); /*----------------------------------------------------------*| |* # MINT *| |*----------------------------------------------------------*/ if (exists(_tokenId)) { /** * @dev since `_tokenId` is a user-supplied parameter it can't be trusted, therefore if increasing supply for an existing token, * the `tokenUri` contained in the `_voucher` MUST match the one stored at `_tokenURIs[_tokenId]`. */ require( voucher.tokenUri == _tokenURIs[_tokenId] && // if the tokenId already exists, increment the array value at _totalSupply[_tokenId] (_totalSupply[_tokenId] += _value) <= voucher.totalValue ); } else { require(_value <= voucher.totalValue); /// @dev since `_tokenId` is user-supplied it MUST be reassigned the correct value for the rest of the function work correctly _tokenId = _totalSupply.length; // if the tokenId doesn't exist yet, push a new value to the corresponding array index _totalSupply.push(_value); _tokenURIs[_tokenId] = voucher.tokenUri; } /*----------------------------------------------------------*| |* # UPDATE BALANCES *| |*----------------------------------------------------------*/ /** * @dev the following is the net state change after tokenId `_value` has been minted and transferred to a buyer, * rather than calling the internal `_mint` function which whould increase balance of minter, followed by `safeTransferFrom`, which would decrease it. * I.e. minter balance is not updated as it would cancel out anyway after sending the newly minted tokenId `_value` to buyer. * Additionally `safeTransferFrom` function has been omitted otherwise `msg.sender` would have to be an authorized operator by the seller/minter. */ unchecked { _balanceOf[_to][_tokenId] += _value; } // event is needed in order to signal to DApps that a mint has occurred emit TransferSingle(msg.sender, address(0), signer, _tokenId, _value); // event is needed in order to signal to DApps that a token transfer has occurred emit TransferSingle(msg.sender, signer, _to, _tokenId, _value); /*----------------------------------------------------------*| |* # PAY MARKET FEES *| |*----------------------------------------------------------*/ uint256 marketFeeAmount = (msg.value * voucher.marketFeeBps) / 10000; sellerAmount -= marketFeeAmount; _sendValue(voucher.marketFeeRecipient, marketFeeAmount); /*----------------------------------------------------------*| |* # CHECK-EFFECTS-INTERACTIONS *| |*----------------------------------------------------------*/ /// @dev perform external calls to untrusted addresses last /*----------------------------------------------------------*| |* # PAY SELLER (AND COMMISSIONS) *| |*----------------------------------------------------------*/ if (voucher.saleCommissionBps > 0) { uint256 commissionAmount = (msg.value * voucher.saleCommissionBps) / 10000; sellerAmount -= commissionAmount; _sendValue(voucher.saleCommissionRecipient, commissionAmount); } _sendValue(signer, sellerAmount); /*----------------------------------------------------------*| |* # SAFE TRANSFER *| |*----------------------------------------------------------*/ /// @dev onERC1155Received is not called on the minter's account if (_to.code.length > 0) require( IERC1155Receiver(_to).onERC1155Received( msg.sender, // operator signer, // from _tokenId, // token id _value, // value _data ) == 0xf23a6e61, // IERC1155Receiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } /** * @dev Creates `amount` new tokens for `to`, of token type `id`. * @param _to if different from msg.sender it is considered an airdrop * @param _tokenURI the ipfs hash of the token, base58 decoded, then the first two bytes "Qm" removed, then hex encoded and in order to fit exactly in 32 bytes (uint256 is 32 bytes). * * const getBytes32FromIpfsHash = hash => { * let bytes = bs58.decode(hash); * bytes = bytes.slice(2, bytes.length); * let hexString = web3.utils.bytesToHex(bytes); * return web3.utils.hexToNumber(hexString); * }; * * See {ERC1155-_mint}. * * Requirements: * * - the caller must have the `MINTER_ROLE`, i.e. only the owner of the collection can mint tokens, not just any artist on Ninfa marketplace. * */ function mint( address _to, bytes32 _tokenURI, uint256 _amount, bytes memory _data ) external onlyRole(MINTER_ROLE) { uint256 tokenId = _totalSupply.length; _mint(_to, tokenId, _amount, _data); _tokenURIs[tokenId] = _tokenURI; _totalSupply.push(_amount); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. */ function mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) external onlyRole(MINTER_ROLE) { _mintBatch(to, ids, amounts, data); } /*----------------------------------------------------------*| |* # BURN *| |*----------------------------------------------------------*/ function burn(address _from, uint256 _id, uint256 _value) public override { super.burn(_from, _id, _value); /// @dev since balance has already been decremented without underflow, // `_totalSupply` may be safely decremented. See {ERC1155-_burn} unchecked { _totalSupply[_id] -= _value; } // if all supply has been burned, _tokenURIs[_id] is deleted from storage if (_totalSupply[_id] == 0) delete _tokenURIs[_id]; } /*----------------------------------------------------------*| |* # PRIVATE FUNCTIONS *| |*----------------------------------------------------------*/ function _recover( Voucher calldata _voucher, bytes memory _signature ) private view returns (address _signer) { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( VOUCHER_TYPEHASH, _voucher.tokenUri, _voucher.unitPrice, _voucher.totalValue, _voucher.endTime, _voucher.saleCommissionBps, _voucher.marketFeeBps, _voucher.saleCommissionRecipient, _voucher.marketFeeRecipient ) ) ) ); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(_signature, 0x20)) s := mload(add(_signature, 0x40)) v := byte(0, mload(add(_signature, 0x60))) } _signer = ecrecover(digest, v, r, s); if (_signer == address(0)) revert(); } function _sendValue(address _receiver, uint256 _amount) private { (bool success, ) = payable(_receiver).call{value: _amount}(""); require(success); } /*----------------------------------------------------------*| |* # ERC-165 LOGIC *| |*----------------------------------------------------------*/ function supportsInterface( bytes4 interfaceId ) external pure returns (bool) { return interfaceId == 0x01ffc9a7 || // Interface ID for IERC165 interfaceId == 0xd9b67a26 || // Interface ID for IERC1155 interfaceId == 0x0e89341c || // Interface ID for IERC1155MetadataURI interfaceId == 0x2a55205a || // Interface ID for IERC2981 interfaceId == 0x7965db0b; // Interface ID for IAccessControl } /*----------------------------------------------------------*| |* # ADMIN FUNCTIONS *| |*----------------------------------------------------------*/ function setRoyaltyInfo( address royaltyRecipient_, uint24 royaltyBps_ ) external onlyRole(DEFAULT_ADMIN_ROLE) { _royaltyRecipient = payable(royaltyRecipient_); _royaltyBps = royaltyBps_; } /** * @notice This function is used when the artist decides to set the royalty receiver to an address other than its own. * It adds the artist address to the `artists` mapping in {ERC2981Communal}, in order to use it for access control in `setRoyaltyRecipient()`. This removes the burden of setting this mapping in the `mint()` function as it will rarely be needed. * @param royaltyRecipient_ (likely a payment splitter contract) may be 0x0 although it is not intended as ETH would be burnt if sent to 0x0. If the user only wants to mint it should call mint() instead, so that the roy * * Require: * * - If the `artists` for `_tokenId` mapping is empty, the minter's address is equal to `royaltyRecipients[_tokenId]`. I.e. the caller must correspond to `royaltyRecipients[_tokenId]`, i.e. the token minter/artist * - Else, the caller must correspond to the `_tokenId`'s minter address set in `artists[_tokenId]`, i.e. if `artists[_tokenId]` is not 0x0. Note that the artist address cannot be reset. * * Allow: * * - the minter may (re)set `royaltyRecipients[_tokenId]` to the same address as `artists[_tokenId]`, i.e. the minter/artist. This would be quite useless, but not dangerous. The frontend should disallow it. * */ function setRoyaltyRecipient( address royaltyRecipient_ ) external onlyRole(DEFAULT_ADMIN_ROLE) { _royaltyRecipient = payable(royaltyRecipient_); } function setRoyaltyBps( uint24 royaltyBps_ ) external onlyRole(DEFAULT_ADMIN_ROLE) { _royaltyBps = royaltyBps_; } /*----------------------------------------------------------*| |* # POST-DEPLOYMENT INITIALIZATION *| |*----------------------------------------------------------*/ function initialize(bytes calldata _data) external { require(msg.sender == _FACTORY); address owner; (name, symbol, owner, _royaltyRecipient, _royaltyBps) = abi.decode( _data, (string, string, address, address, uint24) ); /** * @dev The EIP712Domain fields should be the order as above, skipping any absent fields. * Protocol designers only need to include the fields that make sense for their signing domain. Unused fields are left out of the struct type. * @param name the user readable name of signing domain, i.e. the name of the DApp or the protocol. * @param chainId the EIP-155 chain id. The user-agent should refuse signing if it does not match the currently active chain. * @param verifyingContract the address of the contract that will verify the signature. The user-agent may do contract specific phishing prevention. * verifyingContract is the only variable parameter in the DOMAIN_SEPARATOR in order to avoid signature replay across different contracts * therefore the DOMAIN_SEPARATOR MUST be calculated inside of the `initialize` function rather than the constructor. */ DOMAIN_SEPARATOR = keccak256( abi.encode( 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866, //harcoded value for keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)") DOMAIN_TYPEHASH 0xdb3dd9b854cdb7551722584c7e89b5df9798432c0c9ee9bc6f62a8edfed5dac4, //harcoded value for keccak256(bytes("ninfa.io")), block.chainid, address(this) ) ); _grantRole(DEFAULT_ADMIN_ROLE, owner); // DEFAULT_ADMIN_ROLE is by default admin of all other roles, i.e. MINTER_ROLE, meaning it can assign MINTER_ROLE to other addresses _grantRole(MINTER_ROLE, msg.sender); // grant MINTER_ROLE to factory contract _grantRole(MINTER_ROLE, owner); // grant MINTER_ROLE to owner, this way minting requires only checking for MINTER_ROLE rather than also DEFAULT_ADMIN_ROLE. } /** * @notice creates `DOMAIN_SEPARATOR` and `VOUCHER_TYPEHASH` and assigns address to `_FACTORY` * @param factory_ is used for access control on self-sovreign ERC-1155 collection rather than using the `initializer` modifier, * this is cheaper because the clones won't need to write `initialized = true;` to storage each time they are initialized * instead `_FACTORY` is only assigned once in the `constructor` of the master copy therefore it can be read by all clones. */ constructor(address factory_) { VOUCHER_TYPEHASH = keccak256( "Voucher(bytes32 tokenUri,uint256 unitPrice,uint256 totalValue,uint256 endTime,uint256 saleCommissionBps,uint256 marketFeeBps,address saleCommissionRecipient,address marketFeeRecipient)" ); _FACTORY = factory_; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity 0.8.17; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /// @dev stripped down version of https://github.com/MrChico/verifyIPFS/ library DecodeTokenURI { bytes constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; /** * @dev Converts hex string to base 58 */ function toBase58(bytes memory source) internal pure returns (bytes memory) { if (source.length == 0) return new bytes(0); uint8[] memory digits = new uint8[](64); digits[0] = 0; uint8 digitlength = 1; for (uint256 i = 0; i < source.length; ++i) { uint256 carry = uint8(source[i]); for (uint256 j = 0; j < digitlength; ++j) { carry += uint256(digits[j]) * 256; digits[j] = uint8(carry % 58); carry = carry / 58; } while (carry > 0) { digits[digitlength] = uint8(carry % 58); digitlength++; carry = carry / 58; } } return toAlphabet(reverse(truncate(digits, digitlength))); } function toAlphabet(uint8[] memory indices) private pure returns (bytes memory) { bytes memory output = new bytes(indices.length); for (uint256 i = 0; i < indices.length; i++) { output[i] = ALPHABET[indices[i]]; } return output; } function truncate(uint8[] memory array, uint8 length) private pure returns (uint8[] memory) { uint8[] memory output = new uint8[](length); for (uint256 i = 0; i < length; i++) { output[i] = array[i]; } return output; } function reverse(uint8[] memory input) private pure returns (uint8[] memory) { uint8[] memory output = new uint8[](input.length); for (uint256 i = 0; i < input.length; i++) { output[i] = input[input.length - 1 - i]; } return output; } }
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; /************************************************************* * @title ERC2981PersonalEditions * * * * @notice Adds ERC2981 support to {ERC1155} * * * * @dev {ERC2981} royalties for self-sovreign collections * * * * @custom:security-contact [email protected] * ************************************************************/ contract ERC2981PersonalEditions { uint24 private constant TOTAL_SHARES = 10000; // 10,000 = 100% (total sale price) uint24 internal _royaltyBps; /// @notice the royaltyRecipient or contract deployer. address payable internal _royaltyRecipient; /** * @notice Called with the sale price to determine how much royalty * is owed and to whom. * @param _salePrice - the sale price of the NFT asset specified by _tokenId * @return recipient - address of who should be sent the royalty payment * @return royaltyAmount - the royalty payment amount for _salePrice */ function royaltyInfo( uint256, uint256 _salePrice ) external view returns (address, uint256) { return (_royaltyRecipient, (_salePrice * _royaltyBps) / TOTAL_SHARES); } }
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../ERC1155.sol"; import "../../../utils/DecodeTokenURI.sol"; /** * @dev ERC1155 token with storage based token URI management. */ abstract contract ERC1155URIStorage is ERC1155 { using DecodeTokenURI for bytes; /** * @dev _baseURI is hardcoded and cannot be modified, as the expected token URI MUST be an IPFSv1 hash */ string private _baseURI = "ipfs://"; /** * @dev Optional mapping for token URIs. Internal as needs to be read by child implementation * returns bytes32 IPFS hash */ mapping(uint256 => bytes32) internal _tokenURIs; /** * @dev Returns the URI for token type `id`. */ function uri(uint256 tokenId) public view returns (string memory) { require(_tokenURIs[tokenId] != 0x00, "ERC1155: nonexistent token"); return string( // once hex decoded base58 is converted to string, we get the initial IPFS hash abi.encodePacked( _baseURI, abi .encodePacked( // full bytes of base58 + hex encoded IPFS hash example. bytes2(0x1220), // prepending 2 bytes IPFS hash identifier that was removed before storing the hash in order to fit in bytes32. 0x1220 is "Qm" base58 and hex encoded _tokenURIs[tokenId] // bytes32(tokenId) // tokenURI (IPFS hash) with its first 2 bytes truncated, base58 and hex encoded returned as bytes32 ).toBase58() ) ); } }
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../ERC1155.sol"; /** * @dev Extension of ERC1155 that adds tracking of total supply per id. * * Useful for scenarios where Fungible and Non-fungible tokens have to be * clearly identified. Note: While a totalSupply of 1 might mean the * corresponding is an NFT, there is no guarantees that no other token with the * same id are not going to be minted. * */ abstract contract ERC1155Supply is ERC1155 { /** * @dev keeps track of per-token id's total supply, as well as overall supply. * also used as a counter when minting, by reading the .length property of the array. * @dev toalSupply MUST be incremented by the implementing contract, as `_beforeTokenTransfer` function * has been removed in order to make normal (non-mint) transfers cheaper. */ uint256[] internal _totalSupply; /** * @dev Total amount of tokens in with a given _id. * @dev > The total value transferred from address 0x0 minus the total value transferred to 0x0 observed via the TransferSingle and TransferBatch events MAY be used by clients and exchanges to determine the “circulating supply” for a given token ID. */ function totalSupply(uint256 _id) public view returns (uint256) { return _totalSupply[_id]; } /** * @dev Amount of unique token ids in this collection, required in order to * enumerate `_totalSupply` (or `_tokenURIs`, see {ERC1155URIStorage-uri}) from a client */ function totalSupply() external view returns (uint256) { return _totalSupply.length; } /** * @dev Indicates whether any token exist with a given _id, or not. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function exists(uint256 _id) public view returns (bool) { return _totalSupply.length > _id; } }
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "../ERC1155.sol"; /************************************************************* * @title ERC1155Burnable * * * * @notice ERC-1155 burnable extension * * * * @custom:security-contact [email protected] * ************************************************************/ contract ERC1155Burnable is ERC1155 { function burn(address _from, uint256 _id, uint256 _value) public virtual { require(msg.sender == _from || isApprovedForAll[_from][msg.sender]); _burn(_from, _id, _value); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IERC1155Receiver { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
/*----------------------------------------------------------*| |* ███ ██ ██ ███ ██ ███████ █████ *| |* ████ ██ ██ ████ ██ ██ ██ ██ *| |* ██ ██ ██ ██ ██ ██ ██ █████ ███████ *| |* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ *| |* ██ ████ ██ ██ ████ ██ ██ ██ *| |*----------------------------------------------------------*/ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./IERC1155Receiver.sol"; /************************************************************* * @title ERC1155 * * * * @notice Gas efficient standard ERC1155 implementation. * * * * @author Fork of solmate ERC1155 * * https://github.com/Rari-Capital/solmate/ * * * * @dev includes `_totalSupply` array needed in order to * * implement a maxSupply limit for lazy minting * * * * @custom:security-contact [email protected] * ************************************************************/ contract ERC1155 { /*----------------------------------------------------------*| |* # EVENTS *| |*----------------------------------------------------------*/ event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value ); event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); event ApprovalForAll( address indexed owner, address indexed operator, bool approved ); event URI(string value, uint256 indexed id); /*----------------------------------------------------------*| |* # ERC-1155 STORAGE LOGIC *| |*----------------------------------------------------------*/ mapping(address => mapping(uint256 => uint256)) internal _balanceOf; mapping(address => mapping(address => bool)) public isApprovedForAll; // Mapping from account to operator approvals /*----------------------------------------------------------*| |* # ERC-1155 LOGIC *| |*----------------------------------------------------------*/ function setApprovalForAll(address operator, bool approved) public { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) public { require( msg.sender == from || isApprovedForAll[from][msg.sender], "ERC1155: NOT_AUTHORIZED" ); _balanceOf[from][id] -= amount; _balanceOf[to][id] += amount; emit TransferSingle(msg.sender, from, to, id, amount); require( to.code.length == 0 ? to != address(0) : IERC1155Receiver(to).onERC1155Received( msg.sender, from, id, amount, data ) == IERC1155Receiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external { require(ids.length == amounts.length, "LENGTH_MISMATCH"); require( msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED" ); // Storing these outside the loop saves ~15 gas per iteration. uint256 id; uint256 amount; for (uint256 i = 0; i < ids.length; ) { id = ids[i]; amount = amounts[i]; _balanceOf[from][id] -= amount; _balanceOf[to][id] += amount; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, from, to, ids, amounts); require( to.code.length == 0 ? to != address(0) : IERC1155Receiver(to).onERC1155BatchReceived( msg.sender, from, ids, amounts, data ) == IERC1155Receiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function balanceOfBatch( address[] calldata owners, uint256[] calldata ids ) external view returns (uint256[] memory balances) { require(owners.length == ids.length, "LENGTH_MISMATCH"); balances = new uint256[](owners.length); // Unchecked because the only math done is incrementing // the array index counter which cannot possibly overflow. unchecked { for (uint256 i = 0; i < owners.length; ++i) { balances[i] = _balanceOf[owners[i]][ids[i]]; } } } function balanceOf( address _owner, uint256 _id ) external view returns (uint256 balance) { balance = _balanceOf[_owner][_id]; } /*----------------------------------------------------------*| |* # INTERNAL MINT/BURN LOGIC *| |*----------------------------------------------------------*/ function _mint( address _to, uint256 _id, uint256 _amount, bytes memory _data ) internal { _balanceOf[_to][_id] += _amount; emit TransferSingle(msg.sender, address(0), _to, _id, _amount); if (_to.code.length > 0) require( IERC1155Receiver(_to).onERC1155Received( msg.sender, address(0), _id, _amount, _data ) == IERC1155Receiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function _mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { _balanceOf[to][ids[i]] += amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, address(0), to, ids, amounts); require( to.code.length == 0 ? to != address(0) : IERC1155Receiver(to).onERC1155BatchReceived( msg.sender, address(0), ids, amounts, data ) == IERC1155Receiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function _burn(address _from, uint256 _id, uint256 _value) internal { // `require(fromBalance >= _value)` is implicitly enforced _balanceOf[_from][_id] -= _value; emit TransferSingle(msg.sender, _from, address(0), _id, _value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol) pragma solidity 0.8.17; import "../utils/Strings.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged( bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole ); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted( bytes32 indexed role, address indexed account, address indexed sender ); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked( bytes32 indexed role, address indexed account, address indexed sender ); struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, msg.sender); _; } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole( bytes32 role, address account ) external onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole( bytes32 role, address account ) external onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external { require( account == msg.sender, "AccessControl: can only renounce roles for self" ); _revokeRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, msg.sender); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, msg.sender); } } }
{ "remappings": [], "optimizer": { "enabled": false, "runs": 200 }, "evmVersion": "london", "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"address","name":"factory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","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":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"bytes","name":"_data","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"tokenUri","type":"bytes32"},{"internalType":"uint256","name":"unitPrice","type":"uint256"},{"internalType":"uint256","name":"totalValue","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"saleCommissionBps","type":"uint256"},{"internalType":"uint256","name":"marketFeeBps","type":"uint256"},{"internalType":"address","name":"saleCommissionRecipient","type":"address"},{"internalType":"address","name":"marketFeeRecipient","type":"address"}],"internalType":"struct ERC1155Minter.Voucher","name":"voucher","type":"tuple"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"lazyMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes32","name":"_tokenURI","type":"bytes32"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mintBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","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":"","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"royaltyBps_","type":"uint24"}],"name":"setRoyaltyBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"royaltyRecipient_","type":"address"},{"internalType":"uint24","name":"royaltyBps_","type":"uint24"}],"name":"setRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"royaltyRecipient_","type":"address"}],"name":"setRoyaltyRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.