ETH Price: $2,168.39 (+1.10%)

Transaction Decoder

Block:
13178847 at Sep-07-2021 01:17:48 PM +UTC
Transaction Fee:
0.004816448947009812 ETH $10.44
Gas Used:
65,282 Gas / 73.779126666 Gwei

Emitted Events:

513 ProvablyRareGemProxy.0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62( 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62, 0x0000000000000000000000002a148bea28c50af6a977a59252497c09bfea1fa1, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000002a148bea28c50af6a977a59252497c09bfea1fa1, 0000000000000000000000000000000000000000000000000000000000000004, 0000000000000000000000000000000000000000000000000000000000000004 )

Account State Difference:

  Address   Before After State Difference Code
0x2a148BEa...9BFEA1fA1
0.708064382404538888 Eth
Nonce: 8
0.703247933457529076 Eth
Nonce: 9
0.004816448947009812
(BeePool)
1,644.933547133920419478 Eth1,644.93368616596401682 Eth0.000139032043597342
0xC67DED0e...B4dAd6dD4

Execution Trace

ProvablyRareGemProxy.071e9503( )
  • ProvablyRareGemV2.mine( kind=4, salt=626302906825169137201804298405996603926038574712601958504214 )
    File 1 of 2: ProvablyRareGemProxy
    // SPDX-License-Identifier: MIT
    
    pragma solidity 0.8.3;
    
    
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/Address
    
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
    
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 0;
        }
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
    
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
    
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
    
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
    
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/IBeacon
    
    /**
     * @dev This is the interface that {BeaconProxy} expects of its beacon.
     */
    interface IBeacon {
        /**
         * @dev Must return an address that can be used as a delegate call target.
         *
         * {BeaconProxy} will check that this address is a contract.
         */
        function implementation() external view returns (address);
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/Proxy
    
    /**
     * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
     * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
     * be specified by overriding the virtual {_implementation} function.
     *
     * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
     * different contract through the {_delegate} function.
     *
     * The success and return data of the delegated call will be returned back to the caller of the proxy.
     */
    abstract contract Proxy {
        /**
         * @dev Delegates the current call to `implementation`.
         *
         * This function does not return to its internall call site, it will return directly to the external caller.
         */
        function _delegate(address implementation) internal virtual {
            assembly {
                // Copy msg.data. We take full control of memory in this inline assembly
                // block because it will not return to Solidity code. We overwrite the
                // Solidity scratch pad at memory position 0.
                calldatacopy(0, 0, calldatasize())
    
                // Call the implementation.
                // out and outsize are 0 because we don't know the size yet.
                let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
    
                // Copy the returned data.
                returndatacopy(0, 0, returndatasize())
    
                switch result
                // delegatecall returns 0 on error.
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
            }
        }
    
        /**
         * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
         * and {_fallback} should delegate.
         */
        function _implementation() internal view virtual returns (address);
    
        /**
         * @dev Delegates the current call to the address returned by `_implementation()`.
         *
         * This function does not return to its internall call site, it will return directly to the external caller.
         */
        function _fallback() internal virtual {
            _beforeFallback();
            _delegate(_implementation());
        }
    
        /**
         * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
         * function in the contract matches the call data.
         */
        fallback() external payable virtual {
            _fallback();
        }
    
        /**
         * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
         * is empty.
         */
        receive() external payable virtual {
            _fallback();
        }
    
        /**
         * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
         * call, or as part of the Solidity `fallback` or `receive` functions.
         *
         * If overriden should call `super._beforeFallback()`.
         */
        function _beforeFallback() internal virtual {}
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/StorageSlot
    
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     *
     * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
    
        struct BooleanSlot {
            bool value;
        }
    
        struct Bytes32Slot {
            bytes32 value;
        }
    
        struct Uint256Slot {
            uint256 value;
        }
    
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            assembly {
                r.slot := slot
            }
        }
    
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            assembly {
                r.slot := slot
            }
        }
    
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            assembly {
                r.slot := slot
            }
        }
    
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            assembly {
                r.slot := slot
            }
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/ERC1967Upgrade
    
    /**
     * @dev This abstract contract provides getters and event emitting update functions for
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
     *
     * _Available since v4.1._
     *
     * @custom:oz-upgrades-unsafe-allow delegatecall
     */
    abstract contract ERC1967Upgrade {
        // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
        bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
    
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
        /**
         * @dev Emitted when the implementation is upgraded.
         */
        event Upgraded(address indexed implementation);
    
        /**
         * @dev Returns the current implementation address.
         */
        function _getImplementation() internal view returns (address) {
            return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
        }
    
        /**
         * @dev Stores a new address in the EIP1967 implementation slot.
         */
        function _setImplementation(address newImplementation) private {
            require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
            StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
        }
    
        /**
         * @dev Perform implementation upgrade
         *
         * Emits an {Upgraded} event.
         */
        function _upgradeTo(address newImplementation) internal {
            _setImplementation(newImplementation);
            emit Upgraded(newImplementation);
        }
    
        /**
         * @dev Perform implementation upgrade with additional setup call.
         *
         * Emits an {Upgraded} event.
         */
        function _upgradeToAndCall(
            address newImplementation,
            bytes memory data,
            bool forceCall
        ) internal {
            _upgradeTo(newImplementation);
            if (data.length > 0 || forceCall) {
                Address.functionDelegateCall(newImplementation, data);
            }
        }
    
        /**
         * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
         *
         * Emits an {Upgraded} event.
         */
        function _upgradeToAndCallSecure(
            address newImplementation,
            bytes memory data,
            bool forceCall
        ) internal {
            address oldImplementation = _getImplementation();
    
            // Initial upgrade and setup call
            _setImplementation(newImplementation);
            if (data.length > 0 || forceCall) {
                Address.functionDelegateCall(newImplementation, data);
            }
    
            // Perform rollback test if not already in progress
            StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
            if (!rollbackTesting.value) {
                // Trigger rollback using upgradeTo from the new implementation
                rollbackTesting.value = true;
                Address.functionDelegateCall(
                    newImplementation,
                    abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
                );
                rollbackTesting.value = false;
                // Check rollback was effective
                require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                // Finally reset to the new implementation and log the upgrade
                _upgradeTo(newImplementation);
            }
        }
    
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
    
        /**
         * @dev Emitted when the admin account has changed.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
    
        /**
         * @dev Returns the current admin.
         */
        function _getAdmin() internal view returns (address) {
            return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
        }
    
        /**
         * @dev Stores a new address in the EIP1967 admin slot.
         */
        function _setAdmin(address newAdmin) private {
            require(newAdmin != address(0), "ERC1967: new admin is the zero address");
            StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
        }
    
        /**
         * @dev Changes the admin of the proxy.
         *
         * Emits an {AdminChanged} event.
         */
        function _changeAdmin(address newAdmin) internal {
            emit AdminChanged(_getAdmin(), newAdmin);
            _setAdmin(newAdmin);
        }
    
        /**
         * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
         * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
         */
        bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
    
        /**
         * @dev Emitted when the beacon is upgraded.
         */
        event BeaconUpgraded(address indexed beacon);
    
        /**
         * @dev Returns the current beacon.
         */
        function _getBeacon() internal view returns (address) {
            return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
        }
    
        /**
         * @dev Stores a new beacon in the EIP1967 beacon slot.
         */
        function _setBeacon(address newBeacon) private {
            require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
            require(
                Address.isContract(IBeacon(newBeacon).implementation()),
                "ERC1967: beacon implementation is not a contract"
            );
            StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
        }
    
        /**
         * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
         * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
         *
         * Emits a {BeaconUpgraded} event.
         */
        function _upgradeBeaconToAndCall(
            address newBeacon,
            bytes memory data,
            bool forceCall
        ) internal {
            _setBeacon(newBeacon);
            emit BeaconUpgraded(newBeacon);
            if (data.length > 0 || forceCall) {
                Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
            }
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/ERC1967Proxy
    
    /**
     * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
     * implementation address that can be changed. This address is stored in storage in the location specified by
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
     * implementation behind the proxy.
     */
    contract ERC1967Proxy is Proxy, ERC1967Upgrade {
        /**
         * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
         *
         * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
         * function call, and allows initializating the storage of the proxy like a Solidity constructor.
         */
        constructor(address _logic, bytes memory _data) payable {
            assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
            _upgradeToAndCall(_logic, _data, false);
        }
    
        /**
         * @dev Returns the current implementation address.
         */
        function _implementation() internal view virtual override returns (address impl) {
            return ERC1967Upgrade._getImplementation();
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/TransparentUpgradeableProxy
    
    /**
     * @dev This contract implements a proxy that is upgradeable by an admin.
     *
     * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
     * clashing], which can potentially be used in an attack, this contract uses the
     * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
     * things that go hand in hand:
     *
     * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
     * that call matches one of the admin functions exposed by the proxy itself.
     * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
     * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
     * "admin cannot fallback to proxy target".
     *
     * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
     * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
     * to sudden errors when trying to call a function from the proxy implementation.
     *
     * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
     * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
     */
    contract TransparentUpgradeableProxy is ERC1967Proxy {
        /**
         * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
         * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
         */
        constructor(
            address _logic,
            address admin_,
            bytes memory _data
        ) payable ERC1967Proxy(_logic, _data) {
            assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
            _changeAdmin(admin_);
        }
    
        /**
         * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
         */
        modifier ifAdmin() {
            if (msg.sender == _getAdmin()) {
                _;
            } else {
                _fallback();
            }
        }
    
        /**
         * @dev Returns the current admin.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
         * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
         */
        function admin() external ifAdmin returns (address admin_) {
            admin_ = _getAdmin();
        }
    
        /**
         * @dev Returns the current implementation.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
         * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
         */
        function implementation() external ifAdmin returns (address implementation_) {
            implementation_ = _implementation();
        }
    
        /**
         * @dev Changes the admin of the proxy.
         *
         * Emits an {AdminChanged} event.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
         */
        function changeAdmin(address newAdmin) external virtual ifAdmin {
            _changeAdmin(newAdmin);
        }
    
        /**
         * @dev Upgrade the implementation of the proxy.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
            _upgradeToAndCall(newImplementation, bytes(""), false);
        }
    
        /**
         * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
         * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
         * proxied contract.
         *
         * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
            _upgradeToAndCall(newImplementation, data, true);
        }
    
        /**
         * @dev Returns the current admin.
         */
        function _admin() internal view virtual returns (address) {
            return _getAdmin();
        }
    
        /**
         * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
         */
        function _beforeFallback() internal virtual override {
            require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
            super._beforeFallback();
        }
    }
    
    // File: ProvablyRareGemProxy.sol
    
    contract ProvablyRareGemProxy is TransparentUpgradeableProxy {
      constructor(
        address _logic,
        address admin_,
        bytes memory _data
      ) payable TransparentUpgradeableProxy(_logic, admin_, _data) {}
    }
    

    File 2 of 2: ProvablyRareGemV2
    // SPDX-License-Identifier: MIT
    
    pragma solidity 0.8.3;
    
    
    
    // Part: Base64
    
    /// @title Base64
    /// @notice Provides a function for encoding some bytes in base64
    /// @author Brecht Devos <brecht@loopring.org>
    library Base64 {
      bytes internal constant TABLE =
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    
      /// @notice Encodes some bytes to the base64 representation
      function encode(bytes memory data) internal pure returns (string memory) {
        uint len = data.length;
        if (len == 0) return '';
        // multiply by 4/3 rounded up
        uint encodedLen = 4 * ((len + 2) / 3);
        // Add some extra buffer at the end
        bytes memory result = new bytes(encodedLen + 32);
        bytes memory table = TABLE;
        assembly {
          let tablePtr := add(table, 1)
          let resultPtr := add(result, 32)
          for {
            let i := 0
          } lt(i, len) {
    
          } {
            i := add(i, 3)
            let input := and(mload(add(data, i)), 0xffffff)
            let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
            out := shl(224, out)
            mstore(resultPtr, out)
            resultPtr := add(resultPtr, 4)
          }
          switch mod(len, 3)
          case 1 {
            mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
          }
          case 2 {
            mstore(sub(resultPtr, 1), shl(248, 0x3d))
          }
          mstore(result, encodedLen)
        }
        return string(result);
      }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/Address
    
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
    
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 0;
        }
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
    
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
    
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
    
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
    
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/Context
    
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/IERC165
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/Initializable
    
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        bool private _initialized;
    
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
    
        /**
         * @dev Modifier to protect an initializer function from being invoked twice.
         */
        modifier initializer() {
            require(_initializing || !_initialized, "Initializable: contract is already initialized");
    
            bool isTopLevelCall = !_initializing;
            if (isTopLevelCall) {
                _initializing = true;
                _initialized = true;
            }
    
            _;
    
            if (isTopLevelCall) {
                _initializing = false;
            }
        }
    }
    
    // Part: Strings
    
    library Strings {
      bytes16 private constant _HEX_SYMBOLS = '0123456789abcdef';
    
      /**
       * @dev Converts a `uint256` to its ASCII `string` decimal representation.
       */
      function toString(uint 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';
        }
        uint temp = value;
        uint digits;
        while (temp != 0) {
          digits++;
          temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
          digits -= 1;
          buffer[digits] = bytes1(uint8(48 + uint(value % 10)));
          value /= 10;
        }
        return string(buffer);
      }
    
      /**
       * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
       */
      function toHexString(uint value) internal pure returns (string memory) {
        if (value == 0) {
          return '0x00';
        }
        uint temp = value;
        uint 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(uint value, uint length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = '0';
        buffer[1] = 'x';
        for (uint 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);
      }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/ERC165
    
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/IERC1155
    
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155 is IERC165 {
        /**
         * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
         */
        event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    
        /**
         * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
         * transfers.
         */
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] values
        );
    
        /**
         * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
         * `approved`.
         */
        event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    
        /**
         * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
         *
         * If an {URI} event was emitted for `id`, the standard
         * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
         * returned by {IERC1155MetadataURI-uri}.
         */
        event URI(string value, uint256 indexed id);
    
        /**
         * @dev Returns the amount of tokens of token type `id` owned by `account`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) external view returns (uint256);
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
            external
            view
            returns (uint256[] memory);
    
        /**
         * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the caller.
         */
        function setApprovalForAll(address operator, bool approved) external;
    
        /**
         * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address account, address operator) external view returns (bool);
    
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) external;
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) external;
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/IERC1155Receiver
    
    /**
     * @dev _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
            @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.
            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. 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);
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/IERC1155MetadataURI
    
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\{id\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/ERC1155
    
    /**
     * @dev Implementation of the basic standard multi-token.
     * See https://eips.ethereum.org/EIPS/eip-1155
     * Originally based on code by Enjin: https://github.com/enjin/erc-1155
     *
     * _Available since v3.1._
     */
    contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using Address for address;
    
        // Mapping from token ID to account balances
        mapping(uint256 => mapping(address => uint256)) private _balances;
    
        // Mapping from account to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
    
        // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
        string private _uri;
    
        /**
         * @dev See {_setURI}.
         */
        constructor(string memory uri_) {
            _setURI(uri_);
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
    
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\{id\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) public view virtual override returns (string memory) {
            return _uri;
        }
    
        /**
         * @dev See {IERC1155-balanceOf}.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: balance query for the zero address");
            return _balances[id][account];
        }
    
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
            public
            view
            virtual
            override
            returns (uint256[] memory)
        {
            require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
    
            uint256[] memory batchBalances = new uint256[](accounts.length);
    
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts[i], ids[i]);
            }
    
            return batchBalances;
        }
    
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            require(_msgSender() != operator, "ERC1155: setting approval status for self");
    
            _operatorApprovals[_msgSender()][operator] = approved;
            emit ApprovalForAll(_msgSender(), operator, approved);
        }
    
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[account][operator];
        }
    
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _safeTransferFrom(from, to, id, amount, data);
        }
    
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: transfer caller is not owner nor approved"
            );
            _safeBatchTransferFrom(from, to, ids, amounts, data);
        }
    
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: transfer to the zero address");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
    
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
    
            emit TransferSingle(operator, from, to, id, amount);
    
            _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            require(to != address(0), "ERC1155: transfer to the zero address");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
    
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
    
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
                _balances[id][to] += amount;
            }
    
            emit TransferBatch(operator, from, to, ids, amounts);
    
            _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
        }
    
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\{id\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function _setURI(string memory newuri) internal virtual {
            _uri = newuri;
        }
    
        /**
         * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(
            address account,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(account != address(0), "ERC1155: mint to the zero address");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
    
            _balances[id][account] += amount;
            emit TransferSingle(operator, address(0), account, id, amount);
    
            _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _mintBatch(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
    
            for (uint256 i = 0; i < ids.length; i++) {
                _balances[ids[i]][to] += amounts[i];
            }
    
            emit TransferBatch(operator, address(0), to, ids, amounts);
    
            _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
        }
    
        /**
         * @dev Destroys `amount` tokens of token type `id` from `account`
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens of token type `id`.
         */
        function _burn(
            address account,
            uint256 id,
            uint256 amount
        ) internal virtual {
            require(account != address(0), "ERC1155: burn from the zero address");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
    
            uint256 accountBalance = _balances[id][account];
            require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][account] = accountBalance - amount;
            }
    
            emit TransferSingle(operator, account, address(0), id, amount);
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         */
        function _burnBatch(
            address account,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            require(account != address(0), "ERC1155: burn from the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
    
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
    
                uint256 accountBalance = _balances[id][account];
                require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
                unchecked {
                    _balances[id][account] = accountBalance - amount;
                }
            }
    
            emit TransferBatch(operator, account, address(0), ids, amounts);
        }
    
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `id` and `amount` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
    
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
    
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
    
        function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
            uint256[] memory array = new uint256[](1);
            array[0] = element;
    
            return array;
        }
    }
    
    // Part: OpenZeppelin/openzeppelin-contracts@4.3.0/ERC1155Supply
    
    /**
     * @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 {
        mapping(uint256 => uint256) private _totalSupply;
    
        /**
         * @dev Total amount of tokens in with a given id.
         */
        function totalSupply(uint256 id) public view virtual returns (uint256) {
            return _totalSupply[id];
        }
    
        /**
         * @dev Indicates weither any token exist with a given id, or not.
         */
        function exists(uint256 id) public view virtual returns (bool) {
            return ERC1155Supply.totalSupply(id) > 0;
        }
    
        /**
         * @dev See {ERC1155-_mint}.
         */
        function _mint(
            address account,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual override {
            super._mint(account, id, amount, data);
            _totalSupply[id] += amount;
        }
    
        /**
         * @dev See {ERC1155-_mintBatch}.
         */
        function _mintBatch(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual override {
            super._mintBatch(to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; ++i) {
                _totalSupply[ids[i]] += amounts[i];
            }
        }
    
        /**
         * @dev See {ERC1155-_burn}.
         */
        function _burn(
            address account,
            uint256 id,
            uint256 amount
        ) internal virtual override {
            super._burn(account, id, amount);
            _totalSupply[id] -= amount;
        }
    
        /**
         * @dev See {ERC1155-_burnBatch}.
         */
        function _burnBatch(
            address account,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual override {
            super._burnBatch(account, ids, amounts);
            for (uint256 i = 0; i < ids.length; ++i) {
                _totalSupply[ids[i]] -= amounts[i];
            }
        }
    }
    
    // File: ProvablyRareGemV2.sol
    
    /// @title Provably Rare Gems
    /// @author Sorawit Suriyakarn (swit.eth / https://twitter.com/nomorebear)
    contract ProvablyRareGemV2 is Initializable, ERC1155Supply {
      event Create(uint indexed kind);
      event Mine(address indexed miner, uint indexed kind);
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      string public name;
    
      struct Gem {
        string name; // Gem name
        string color; // Gem color
        bytes32 entropy; // Additional mining entropy. bytes32(0) means can't mine.
        uint difficulty; // Current difficulity level. Must be non decreasing
        uint gemsPerMine; // Amount of gems to distribute per mine
        uint multiplier; // Difficulty multiplier times 1e4. Must be between 1e4 and 1e10
        address crafter; // Address that can craft gems
        address manager; // Current gem manager
        address pendingManager; // Pending gem manager to be transferred to
      }
    
      uint private lock;
      address public owner;
      mapping(uint => Gem) public gems;
      mapping(address => uint) public nonce;
      uint public gemCount;
    
      constructor() ERC1155('GEM') {}
    
      modifier nonReentrant() {
        require(lock == 1, '!lock');
        lock = 2;
        _;
        lock = 1;
      }
    
      modifier onlyOwner() {
        require(owner == msg.sender, '!owner');
        _;
      }
    
      /// @dev Initializes the contract.
      function initialize() external initializer {
        name = 'Provably Rare Gem';
        lock = 1;
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
      }
    
      /// @dev Transfers owner.
      /// @param _owner The new owner.
      function transferOwnership(address _owner) external onlyOwner {
        owner = _owner;
        emit OwnershipTransferred(msg.sender, _owner);
      }
    
      /// @dev Creates a new gem type. The manager can craft a portion of gems + can premine
      function create(
        string calldata name,
        string calldata color,
        uint difficulty,
        uint gemsPerMine,
        uint multiplier,
        address crafter,
        address manager
      ) external returns (uint) {
        require(difficulty > 0 && difficulty <= 2**128, 'bad difficulty');
        require(gemsPerMine > 0 && gemsPerMine <= 1e6, 'bad gems per mine');
        require(multiplier >= 1e4 && multiplier <= 1e10, 'bad multiplier');
        require(manager != address(0), 'bad manager');
        return _create(name, color, difficulty, gemsPerMine, multiplier, crafter, manager);
      }
    
      /// @dev Mines new gemstones. Puts kind you want to mine + your salt and tests your luck!
      function mine(uint kind, uint salt) external nonReentrant {
        uint val = luck(kind, salt);
        nonce[msg.sender]++;
        require(kind < gemCount, 'gem kind not exist');
        uint diff = gems[kind].difficulty;
        require(val <= type(uint).max / diff, 'salt not good enough');
        gems[kind].difficulty = (diff * gems[kind].multiplier) / 10000 + 1;
        _mint(msg.sender, kind, gems[kind].gemsPerMine, '');
      }
    
      /// @dev Updates gem mining entropy. Can be called by gem manager or crafter.
      function updateEntropy(uint kind, bytes32 entropy) external {
        require(kind < gemCount, 'gem kind not exist');
        require(gems[kind].manager == msg.sender || gems[kind].crafter == msg.sender, 'unauthorized');
        gems[kind].entropy = entropy;
      }
    
      /// @dev Updates gem metadata info. Must only be called by the gem manager.
      function updateGemInfo(
        uint kind,
        string calldata name,
        string calldata color
      ) external {
        require(kind < gemCount, 'gem kind not exist');
        require(gems[kind].manager == msg.sender, 'not gem manager');
        gems[kind].name = name;
        gems[kind].color = color;
      }
    
      /// @dev Updates gem mining information. Must only be called by the gem manager.
      function updateMiningData(
        uint kind,
        uint difficulty,
        uint multiplier,
        uint gemsPerMine
      ) external {
        require(kind < gemCount, 'gem kind not exist');
        require(gems[kind].manager == msg.sender, 'not gem manager');
        require(difficulty > 0 && difficulty <= 2**128, 'bad difficulty');
        require(multiplier >= 1e4 && multiplier <= 1e10, 'bad multiplier');
        require(gemsPerMine > 0 && gemsPerMine <= 1e6, 'bad gems per mine');
        gems[kind].difficulty = difficulty;
        gems[kind].multiplier = multiplier;
        gems[kind].gemsPerMine = gemsPerMine;
      }
    
      /// @dev Renounce management ownership for the given gem kinds.
      function renounceManager(uint[] calldata kinds) external {
        for (uint idx = 0; idx < kinds.length; idx++) {
          uint kind = kinds[idx];
          require(kind < gemCount, 'gem kind not exist');
          require(gems[kind].manager == msg.sender, 'not gem manager');
          gems[kind].manager = address(0);
          gems[kind].pendingManager = address(0);
        }
      }
    
      /// @dev Updates gem crafter. Must only be called by the gem manager.
      function updateCrafter(uint[] calldata kinds, address crafter) external {
        for (uint idx = 0; idx < kinds.length; idx++) {
          uint kind = kinds[idx];
          require(kind < gemCount, 'gem kind not exist');
          require(gems[kind].manager == msg.sender, 'not gem manager');
          gems[kind].crafter = crafter;
        }
      }
    
      /// @dev Transfers management ownership for the given gem kinds to another address.
      function transferManager(uint[] calldata kinds, address to) external {
        for (uint idx = 0; idx < kinds.length; idx++) {
          uint kind = kinds[idx];
          require(kind < gemCount, 'gem kind not exist');
          require(gems[kind].manager == msg.sender, 'not gem manager');
          gems[kind].pendingManager = to;
        }
      }
    
      /// @dev Accepts management position for the given gem kinds.
      function acceptManager(uint[] calldata kinds) external {
        for (uint idx = 0; idx < kinds.length; idx++) {
          uint kind = kinds[idx];
          require(kind < gemCount, 'gem kind not exist');
          require(gems[kind].pendingManager == msg.sender, 'not pending manager');
          gems[kind].pendingManager = address(0);
          gems[kind].manager = msg.sender;
        }
      }
    
      /// @dev Mints gems by crafter. Hopefully, crafter is a good guy. Craft gemsPerMine if amount = 0.
      function craft(
        uint kind,
        uint amount,
        address to
      ) external nonReentrant {
        require(kind < gemCount, 'gem kind not exist');
        require(gems[kind].crafter == msg.sender, 'not gem crafter');
        uint realAmount = amount == 0 ? gems[kind].gemsPerMine : amount;
        _mint(to, kind, realAmount, '');
      }
    
      /// @dev Returns your luck given salt and gem kind. The smaller the value, the more success chance.
      function luck(uint kind, uint salt) public view returns (uint) {
        require(kind < gemCount, 'gem kind not exist');
        bytes32 entropy = gems[kind].entropy;
        require(entropy != bytes32(0), 'no entropy');
        bytes memory data = abi.encodePacked(
          block.chainid,
          entropy,
          address(this),
          msg.sender,
          kind,
          nonce[msg.sender],
          salt
        );
        return uint(keccak256(data));
      }
    
      /// @dev Internal function for creating a new gem kind
      function _create(
        string memory gemName,
        string memory color,
        uint difficulty,
        uint gemsPerMine,
        uint multiplier,
        address crafter,
        address manager
      ) internal returns (uint) {
        uint kind = gemCount++;
        gems[kind] = Gem({
          name: gemName,
          color: color,
          entropy: bytes32(0),
          difficulty: difficulty,
          gemsPerMine: gemsPerMine,
          multiplier: multiplier,
          crafter: crafter,
          manager: manager,
          pendingManager: address(0)
        });
        emit Create(kind);
        return kind;
      }
    
      // prettier-ignore
      function uri(uint kind) public view override returns (string memory) {
        require(kind < gemCount, 'gem kind not exist');
        string memory gemName = string(abi.encodePacked(gems[kind].name, ' #', Strings.toString(kind)));
        string memory color = gems[kind].color;
        string memory output = string(abi.encodePacked(
          '<svg id="Layer_1" x="0px" y="0px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 1080" width="350" height="400"><rect x="0" y="0" width="1080" height="1080" fill="#1a1a1a"/><svg id="Layer_1" x="350" y="350" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 1080" width="350" height="400"><g transform="translate(0 -25)"><g><polygon class="st0" fill="',
          color,
          '" points="679.25,58.27 400.75,58.27 203.82,255.2 203.82,824.8 400.75,1021.73 679.25,1021.73 876.18,824.8 876.18,255.2"></polygon><g class="st1" opacity="0.3"><path d="M679.25,58.27h-278.5L203.82,255.2v569.6l196.93,196.93h278.5L876.18,824.8V255.2L679.25,58.27z M739.56,709.06 l-116.9,116.9H457.34l-116.9-116.9V370.94l116.9-116.9h165.32l116.9,116.9V709.06z"></path></g><g><g><polygon class="st2" fill="none" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" points="679.25,58.27 400.75,58.27 203.82,255.2 203.82,824.8 400.75,1021.73 679.25,1021.73 876.18,824.8  876.18,255.2"></polygon><polygon fill="',
          color,
          '" class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" points="622.66,254.04 457.34,254.04 340.44,370.94 340.44,709.06 457.34,825.96 622.66,825.96  739.56,709.06 739.56,370.94"></polygon><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="400.75" y1="58.27" x2="457.34" y2="254.04"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="679.25" y1="58.27" x2="622.66" y2="254.04"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="203.82" y1="255.2" x2="340.44" y2="370.94"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="739.56" y1="370.94" x2="876.18" y2="255.2"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="739.56" y1="709.06" x2="876.18" y2="824.8"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="622.66" y1="825.96" x2="679.25" y2="1021.73"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="457.34" y1="825.96" x2="400.75" y2="1021.73"></line><line class="st2" stroke-width="10" stroke-miterlimit="10" stroke="#ffffff" x1="340.44" y1="709.06" x2="203.82" y2="824.8"></line></g></g></g></g></svg><text x="50%" y="95%" dominant-baseline="middle" text-anchor="middle" font-size="2.5em" fill="#FFFFFF">',
          gemName,
          '</text></svg>'
        ));
        string memory json = Base64.encode(bytes(string(abi.encodePacked(
          '{ "name": "',
          gemName,
          '", ',
          '"description" : ',
          '"Provably Rare Gem is a permissionless on-chain asset for hardcore collectors to mine and collect. Gems must be mined with off-chain Proof-of-Work. The higher the gem rarity, the more difficult it is to be found. Stats and other functionalities are intentionally omitted for others to interpret.", ',
          '"image": "data:image/svg+xml;base64,',
          Base64.encode(bytes(output)),
          '"}'
        ))));
        return string(abi.encodePacked('data:application/json;base64,', json));
      }
    }